{"version":3,"sources":["../src/index.ts","../src/types/common.ts","../src/types/auth.ts","../src/types/oa.ts","../src/types/user.ts","../src/types/zns.ts","../src/types/article.ts","../src/types/broadcast.ts","../src/types/purchase.ts","../src/constants/zns.constants.ts","../src/types/webhook.ts","../src/utils/type-guards.ts","../src/clients/base-client.ts","../src/clients/zalo-client.ts","../src/services/auth.service.ts","../src/services/oa.service.ts","../src/services/user.service.ts","../src/services/zns.service.ts","../src/services/group-message.service.ts","../src/services/group-management.service.ts","../src/services/article.service.ts","../src/services/video-upload.service.ts","../src/services/consultation.service.ts","../src/services/transaction.service.ts","../src/services/promotion.service.ts","../src/services/general-message.service.ts","../src/services/message-management.service.ts","../src/services/broadcast.service.ts","../src/services/purchase.service.ts","../src/services/anonymous-message.service.ts","../src/services/message-reaction.service.ts","../src/services/miniapp-message.service.ts","../src/zalo-sdk.ts"],"sourcesContent":["/**\r\n * RedAI Zalo SDK - Main entry point\r\n *\r\n * A comprehensive TypeScript SDK for Zalo APIs including:\r\n * - Official Account (OA) API\r\n * - Zalo Notification Service (ZNS)\r\n * - Social API\r\n * - Group Message Framework (GMF)\r\n * - Webhook handling\r\n */\r\n\r\n// Export types\r\nexport * from \"./types/common\";\r\nexport * from \"./types/auth\";\r\nexport * from \"./types/oa\";\r\nexport * from \"./types/user\";\r\nexport * from \"./types/zns\";\r\nexport * from \"./types/article\";\r\nexport * from \"./types/broadcast\";\r\nexport * from \"./types/promotion\";\r\nexport * from \"./types/consultation\";\r\nexport * from \"./types/purchase\";\r\nexport type {\r\n  SocialProfile,\r\n  SocialFriend,\r\n  SocialFriendsList,\r\n  SocialPost,\r\n  SocialFeed,\r\n  SocialMessage,\r\n  SocialConversation,\r\n  SocialAlbum,\r\n  SocialPhoto,\r\n  SocialVideo,\r\n  SocialEvent,\r\n  SocialGroup,\r\n  OAuthConfig,\r\n  AuthorizationRequest,\r\n  TokenRequest,\r\n  RefreshTokenRequest,\r\n  SocialApiError,\r\n  CursorPagination,\r\n  OffsetPagination,\r\n} from \"./types/social\";\r\n\r\n// Export ZNS constants and helpers\r\nexport * from \"./constants/zns.constants\";\r\n\r\n// Export message types (avoiding conflicts with webhook types)\r\nexport type {\r\n  BaseMessage,\r\n  TextMessage,\r\n  ImageMessage,\r\n  FileMessage,\r\n  StickerMessage,\r\n  TemplateMessage as MessageTemplateMessage,\r\n  ReactionMessage as MessageReactionMessage,\r\n  Message,\r\n  MessageRecipient,\r\n  SendMessageRequest,\r\n  SendMessageResponse,\r\n  UploadFileResponse,\r\n  MessageStatus,\r\n  MessageEvent,\r\n  ConsultationTextMessage,\r\n  ConsultationImageMessage,\r\n  ConsultationFileMessage,\r\n  ConsultationStickerMessage,\r\n  ConsultationQuoteMessage,\r\n  ConsultationRequestInfoMessage,\r\n  TransactionMessage,\r\n\r\n  AnonymousTextMessage,\r\n  AnonymousImageMessage,\r\n  AnonymousFileMessage,\r\n  AnonymousStickerMessage,\r\n  MiniAppMessage,\r\n  ExtendedMessage,\r\n  // New message types from api.md\r\n  AnonymousMessageRecipient,\r\n  AnonymousMessageResponse,\r\n  ReactIcon,\r\n  SenderAction,\r\n  MessageReactionResponse,\r\n  MiniAppMessageRequest,\r\n  MiniAppMessageButton,\r\n  MiniAppMessageResponse,\r\n} from \"./types/message\";\r\n\r\n// Export webhook types (avoiding conflicts with message and group types)\r\nexport * from \"./types/webhook\";\r\n\r\n// Export type guards and utilities (selective export to avoid conflicts)\r\nexport {\r\n  UserGroupMessageEvent,\r\n  isUserSendGroupTextEvent,\r\n  isUserSendGroupImageEvent,\r\n  isUserSendGroupVideoEvent,\r\n  isUserSendGroupAudioEvent,\r\n  isUserSendGroupFileEvent,\r\n  isUserGroupMessageEvent,\r\n  isOASendTextEvent,\r\n  isOASendImageEvent,\r\n  isOASendFileEvent,\r\n  isOASendStickerEvent,\r\n  isOASendGifEvent,\r\n  isOASendGroupTextEvent,\r\n  isOASendGroupImageEvent,\r\n  isOASendGroupFileEvent,\r\n  isOASendGroupStickerEvent,\r\n  isOASendGroupGifEvent,\r\n  isFromGroup,\r\n  isFromPersonal,\r\n} from \"./utils/type-guards\";\r\n// Export webhook helper functions\r\nexport {\r\n  isUserMessageEvent,\r\n  isGroupMessageEvent,\r\n  isOAToUserMessageEvent,\r\n  isOAToGroupMessageEvent,\r\n  getMessageDirection,\r\n  getWebhookEventAppId,\r\n  getWebhookEventOaId,\r\n  getWebhookEventSenderId,\r\n  getWebhookEventRecipientId,\r\n  getWebhookEventIdentifiers,\r\n} from \"./types/webhook\";\r\n\r\n// Export group types (avoiding conflicts with webhook types)\r\nexport type {\r\n  GroupMessage,\r\n  GroupTextMessage,\r\n  GroupImageMessage,\r\n  GroupFileMessage,\r\n  GroupStickerMessage,\r\n  GroupMentionMessage,\r\n  GroupMessageResult,\r\n  GroupInfo as GroupManagementInfo,\r\n  GroupMember,\r\n  GroupMemberList,\r\n  GroupSettings,\r\n  GroupJoinRequest,\r\n  GroupJoinRequestList,\r\n  GroupInvitation,\r\n  GroupActivity,\r\n  GroupActivityList,\r\n  GroupStatistics,\r\n  GroupMessageTemplate,\r\n  GroupMessageSchedule,\r\n  GroupBroadcast,\r\n  GroupPermission,\r\n  GroupWebhookEvent as GroupManagementWebhookEvent,\r\n  GroupApiResponse,\r\n  GroupCreateResponse,\r\n  GroupUpdateResponse,\r\n  GroupDeleteResponse,\r\n  GroupMemberActionResponse,\r\n  GroupCreateRequest,\r\n  GroupCreateResult,\r\n  GroupUpdateRequest,\r\n  GroupAvatarUpdateRequest,\r\n  GroupMemberInviteRequest,\r\n  GroupMemberActionRequest,\r\n  GroupAdminActionRequest,\r\n  GroupDeleteRequest,\r\n  GroupPendingMember,\r\n  GroupQuota,\r\n  GroupQuotaAsset,\r\n  GroupRecentChat,\r\n  GroupConversationMessage,\r\n  GroupsOfOAResponse,\r\n  GroupDetailResponse,\r\n  GroupPendingMembersResponse,\r\n  GroupAcceptPendingMembersRequest,\r\n  GroupAcceptPendingMembersResponse,\r\n  GroupRemoveMembersRequest,\r\n  GroupRemoveMembersResponse,\r\n  GroupMembersResponse,\r\n  GroupQuotaMessageRequest,\r\n  GroupQuotaMessageResponse,\r\n  AllGroupMembersResponse,\r\n  GetAllMembersProgress,\r\n  EnhancedGroupMember,\r\n  AllGroupMembersWithDetailsResponse,\r\n  GetAllMembersWithDetailsProgress,\r\n} from \"./types/group\";\r\n// Avoid duplicate exports - UserListResponse is already exported from user.ts\r\nexport type {\r\n  UserProfile as UserManagementProfile,\r\n  UserList as UserManagementList,\r\n  UserTag,\r\n  UserTagList,\r\n  UserNote,\r\n  UserInteraction,\r\n  UserAnalytics,\r\n  UserSegment,\r\n  UserCustomField,\r\n  UserCustomFieldValue,\r\n  UserBehavior,\r\n  UserJourney,\r\n  UserPreference,\r\n  UserActivity,\r\n  UserExport,\r\n  UserImport,\r\n  UserSearch,\r\n  UserSearchResult,\r\n  BulkUserOperation,\r\n} from \"./types/user-management\";\r\n\r\n// Export clients\r\nexport { BaseClient } from \"./clients/base-client\";\r\nexport { ZaloClient } from \"./clients/zalo-client\";\r\n\r\n// Export services\r\nexport { AuthService } from \"./services/auth.service\";\r\nexport { OAService } from \"./services/oa.service\";\r\n// export { MessageService } from \"./services/message.service\"; // Replaced by specialized message services\r\nexport { UserService } from \"./services/user.service\";\r\n// export { WebhookService } from \"./services/webhook.service\";\r\nexport { ZNSService } from \"./services/zns.service\";\r\nexport {\r\n  GroupMessageService,\r\n  GroupMessageItem,\r\n  MultipleGroupsProgressInfo,\r\n  SendMessageListToMultipleGroupsRequest,\r\n  SendMessageListToMultipleGroupsResponse\r\n} from \"./services/group-message.service\";\r\nexport { GroupManagementService } from \"./services/group-management.service\";\r\nexport { ArticleService } from \"./services/article.service\";\r\nexport { VideoUploadService } from \"./services/video-upload.service\";\r\n\r\n// Message Services - Specialized by message type\r\nexport { ConsultationService } from \"./services/consultation.service\";\r\nexport { TransactionService } from \"./services/transaction.service\";\r\nexport { PromotionService } from \"./services/promotion.service\";\r\nexport { GeneralMessageService } from \"./services/general-message.service\";\r\nexport {\r\n  MessageManagementService,\r\n  UploadFileResult,\r\n  UploadImageResult,\r\n  MessageQuotaInfo,\r\n  ConversationMessage,\r\n  Conversation\r\n} from \"./services/message-management.service\";\r\nexport { BroadcastService } from \"./services/broadcast.service\";\r\nexport { PurchaseService } from \"./services/purchase.service\";\r\n\r\n// New services from api.md\r\nexport {\r\n  AnonymousMessageService,\r\n  AnonymousTextMessageRequest,\r\n  AnonymousImageMessageRequest,\r\n  AnonymousFileMessageRequest,\r\n  AnonymousStickerMessageRequest,\r\n  AnonymousImageAttachment,\r\n  AnonymousFileAttachment,\r\n  AnonymousStickerAttachment,\r\n} from \"./services/anonymous-message.service\";\r\nexport {\r\n  MessageReactionService,\r\n  MessageReactionRequest,\r\n  ReactionIcon as MessageReactionIconEnum,\r\n} from \"./services/message-reaction.service\";\r\nexport {\r\n  MiniAppMessageService,\r\n  MiniAppSendMessageRequest,\r\n  MiniAppQuotaType,\r\n  QuotaOwnerType,\r\n} from \"./services/miniapp-message.service\";\r\n\r\n// Export main SDK class\r\nexport { ZaloSDK } from \"./zalo-sdk\";\r\n\r\n// Export default\r\nexport { ZaloSDK as default } from \"./zalo-sdk\";\r\n","/**\r\n * Common types and interfaces for Zalo SDK\r\n */\r\n\r\n/**\r\n * Standard Zalo API response wrapper\r\n */\r\nexport interface ZaloResponse<T = any> {\r\n  /**\r\n   * Error code (0 = success)\r\n   */\r\n  error: number;\r\n\r\n  /**\r\n   * Response message\r\n   */\r\n  message: string;\r\n\r\n  /**\r\n   * Response data\r\n   */\r\n  data?: T;\r\n}\r\n\r\n/**\r\n * Zalo API error response\r\n */\r\nexport interface ZaloErrorResponse {\r\n  /**\r\n   * Error code\r\n   */\r\n  error: number;\r\n\r\n  /**\r\n   * Error name\r\n   */\r\n  error_name?: string;\r\n\r\n  /**\r\n   * Error reason\r\n   */\r\n  error_reason?: string;\r\n\r\n  /**\r\n   * Error description\r\n   */\r\n  error_description?: string;\r\n\r\n  /**\r\n   * Reference documentation link\r\n   */\r\n  ref_doc?: string;\r\n}\r\n\r\n/**\r\n * Union type for Zalo API responses\r\n */\r\nexport type ZaloApiResponse<T> = ZaloResponse<T> | (ZaloErrorResponse & Partial<T>);\r\n\r\n/**\r\n * SDK Configuration\r\n */\r\nexport interface ZaloSDKConfig {\r\n  /**\r\n   * Application ID\r\n   */\r\n  appId: string;\r\n\r\n  /**\r\n   * Application Secret\r\n   */\r\n  appSecret: string;\r\n\r\n  /**\r\n   * API timeout in milliseconds (default: 30000)\r\n   */\r\n  timeout?: number;\r\n\r\n  /**\r\n   * Enable debug logging (default: false)\r\n   */\r\n  debug?: boolean;\r\n\r\n  /**\r\n   * Custom API base URL (optional)\r\n   */\r\n  apiBaseUrl?: string;\r\n\r\n  /**\r\n   * Retry configuration\r\n   */\r\n  retry?: {\r\n    /**\r\n     * Number of retry attempts (default: 3)\r\n     */\r\n    attempts?: number;\r\n\r\n    /**\r\n     * Delay between retries in milliseconds (default: 1000)\r\n     */\r\n    delay?: number;\r\n  };\r\n}\r\n\r\n/**\r\n * HTTP method types\r\n */\r\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n\r\n/**\r\n * Request configuration\r\n */\r\nexport interface RequestConfig {\r\n  /**\r\n   * HTTP method\r\n   */\r\n  method: HttpMethod;\r\n\r\n  /**\r\n   * Request URL\r\n   */\r\n  url: string;\r\n\r\n  /**\r\n   * Request headers\r\n   */\r\n  headers?: Record<string, string>;\r\n\r\n  /**\r\n   * Query parameters\r\n   */\r\n  params?: Record<string, any>;\r\n\r\n  /**\r\n   * Request body data\r\n   */\r\n  data?: any;\r\n\r\n  /**\r\n   * Request timeout\r\n   */\r\n  timeout?: number;\r\n}\r\n\r\n/**\r\n * Pagination parameters\r\n */\r\nexport interface PaginationParams {\r\n  /**\r\n   * Offset for pagination\r\n   */\r\n  offset?: number;\r\n\r\n  /**\r\n   * Number of items per page\r\n   */\r\n  count?: number;\r\n\r\n  /**\r\n   * Maximum items per page (default: 50)\r\n   */\r\n  limit?: number;\r\n}\r\n\r\n/**\r\n * Paginated response\r\n */\r\nexport interface PaginatedResponse<T> {\r\n  /**\r\n   * Array of items\r\n   */\r\n  items: T[];\r\n\r\n  /**\r\n   * Total number of items\r\n   */\r\n  total: number;\r\n\r\n  /**\r\n   * Current offset\r\n   */\r\n  offset: number;\r\n\r\n  /**\r\n   * Number of items in current page\r\n   */\r\n  count: number;\r\n\r\n  /**\r\n   * Whether there are more items\r\n   */\r\n  hasMore: boolean;\r\n\r\n  /**\r\n   * Next offset for pagination\r\n   */\r\n  nextOffset?: number;\r\n}\r\n\r\n/**\r\n * File upload information\r\n */\r\nexport interface FileUpload {\r\n  /**\r\n   * File buffer or stream\r\n   */\r\n  file: Buffer | NodeJS.ReadableStream;\r\n\r\n  /**\r\n   * Original filename\r\n   */\r\n  filename: string;\r\n\r\n  /**\r\n   * MIME type\r\n   */\r\n  mimetype: string;\r\n\r\n  /**\r\n   * File size in bytes\r\n   */\r\n  size?: number;\r\n}\r\n\r\n/**\r\n * Attachment information\r\n */\r\nexport interface Attachment {\r\n  /**\r\n   * Attachment ID from Zalo\r\n   */\r\n  attachment_id: string;\r\n\r\n  /**\r\n   * File URL\r\n   */\r\n  url: string;\r\n\r\n  /**\r\n   * File type\r\n   */\r\n  type?: string;\r\n\r\n  /**\r\n   * File size in bytes\r\n   */\r\n  size?: number;\r\n}\r\n\r\n/**\r\n * SDK Error class\r\n */\r\nexport class ZaloSDKError extends Error {\r\n  public readonly code: number;\r\n  public readonly details?: any;\r\n\r\n  constructor(message: string, code: number = -1, details?: any) {\r\n    super(message);\r\n    this.name = 'ZaloSDKError';\r\n    this.code = code;\r\n    this.details = details;\r\n  }\r\n}\r\n\r\n/**\r\n * Logger interface\r\n */\r\nexport interface Logger {\r\n  debug(message: string, ...args: any[]): void;\r\n  info(message: string, ...args: any[]): void;\r\n  warn(message: string, ...args: any[]): void;\r\n  error(message: string, ...args: any[]): void;\r\n}\r\n\r\n/**\r\n * Default console logger implementation\r\n */\r\nexport class ConsoleLogger implements Logger {\r\n  constructor(private readonly enableDebug: boolean = false) {}\r\n\r\n  debug(message: string, ...args: any[]): void {\r\n    if (this.enableDebug) {\r\n      console.debug(`[ZALO-SDK:DEBUG] ${message}`, ...args);\r\n    }\r\n  }\r\n\r\n  info(message: string, ...args: any[]): void {\r\n    console.info(`[ZALO-SDK:INFO] ${message}`, ...args);\r\n  }\r\n\r\n  warn(message: string, ...args: any[]): void {\r\n    console.warn(`[ZALO-SDK:WARN] ${message}`, ...args);\r\n  }\r\n\r\n  error(message: string, ...args: any[]): void {\r\n    console.error(`[ZALO-SDK:ERROR] ${message}`, ...args);\r\n  }\r\n}\r\n","/**\r\n * Authentication related types and interfaces\r\n */\r\n\r\n/**\r\n * Access token information\r\n */\r\nexport interface AccessToken {\r\n  /**\r\n   * Access token string\r\n   */\r\n  access_token: string;\r\n\r\n  /**\r\n   * Token expiration time in seconds\r\n   */\r\n  expires_in: number;\r\n\r\n  /**\r\n   * Refresh token (if available)\r\n   */\r\n  refresh_token?: string;\r\n}\r\n\r\n/**\r\n * Refresh token response\r\n */\r\nexport interface RefreshTokenResponse {\r\n  /**\r\n   * New access token\r\n   */\r\n  access_token: string;\r\n\r\n  /**\r\n   * Token expiration time in seconds\r\n   */\r\n  expires_in: number;\r\n\r\n  /**\r\n   * New refresh token (if available)\r\n   */\r\n  refresh_token?: string;\r\n}\r\n\r\n/**\r\n * OAuth authorization parameters\r\n */\r\nexport interface OAuthParams {\r\n  /**\r\n   * Application ID\r\n   */\r\n  app_id: string;\r\n\r\n  /**\r\n   * Redirect URI\r\n   */\r\n  redirect_uri: string;\r\n\r\n  /**\r\n   * OAuth state parameter\r\n   */\r\n  state?: string;\r\n\r\n  /**\r\n   * Code verifier for PKCE (Social API)\r\n   */\r\n  code_verifier?: string;\r\n\r\n  /**\r\n   * Code challenge for PKCE (Social API)\r\n   */\r\n  code_challenge?: string;\r\n\r\n  /**\r\n   * Code challenge method for PKCE (Social API)\r\n   */\r\n  code_challenge_method?: 'S256' | 'plain';\r\n}\r\n\r\n/**\r\n * Authorization code exchange parameters\r\n */\r\nexport interface AuthCodeParams {\r\n  /**\r\n   * Application ID\r\n   */\r\n  app_id: string;\r\n\r\n  /**\r\n   * Application secret\r\n   */\r\n  app_secret: string;\r\n\r\n  /**\r\n   * Authorization code\r\n   */\r\n  code: string;\r\n\r\n  /**\r\n   * Redirect URI (must match the one used in authorization)\r\n   */\r\n  redirect_uri: string;\r\n\r\n  /**\r\n   * Code verifier for PKCE (supports both Social API and Official Account API)\r\n   */\r\n  code_verifier?: string;\r\n}\r\n\r\n/**\r\n * Refresh token parameters\r\n */\r\nexport interface RefreshTokenParams {\r\n  /**\r\n   * Application ID\r\n   */\r\n  app_id: string;\r\n\r\n  /**\r\n   * Application secret\r\n   */\r\n  app_secret: string;\r\n\r\n  /**\r\n   * Refresh token\r\n   */\r\n  refresh_token: string;\r\n}\r\n\r\n/**\r\n * Social user information from Zalo\r\n */\r\nexport interface SocialUserInfo {\r\n  /**\r\n   * User ID\r\n   */\r\n  id: string;\r\n\r\n  /**\r\n   * Display name\r\n   */\r\n  name: string;\r\n\r\n  /**\r\n   * Profile picture\r\n   */\r\n  picture?: {\r\n    data: {\r\n      url: string;\r\n    };\r\n  };\r\n\r\n  /**\r\n   * Gender\r\n   */\r\n  gender?: string;\r\n\r\n  /**\r\n   * Birthday\r\n   */\r\n  birthday?: string;\r\n\r\n  /**\r\n   * Location\r\n   */\r\n  location?: {\r\n    name: string;\r\n  };\r\n\r\n  /**\r\n   * Whether the account is sensitive (under 18)\r\n   */\r\n  is_sensitive?: boolean;\r\n}\r\n\r\n/**\r\n * Token validation result\r\n */\r\nexport interface TokenValidation {\r\n  /**\r\n   * Whether the token is valid\r\n   */\r\n  valid: boolean;\r\n\r\n  /**\r\n   * Token expiration timestamp\r\n   */\r\n  expires_at?: number;\r\n\r\n  /**\r\n   * Associated user information (if available)\r\n   */\r\n  user_info?: SocialUserInfo;\r\n}\r\n\r\n/**\r\n * Authentication scope for different APIs\r\n */\r\nexport enum AuthScope {\r\n  /**\r\n   * Official Account API scope\r\n   */\r\n  OA = 'oa',\r\n\r\n  /**\r\n   * Social API scope\r\n   */\r\n  SOCIAL = 'social',\r\n\r\n  /**\r\n   * ZNS (Zalo Notification Service) scope\r\n   */\r\n  ZNS = 'zns',\r\n}\r\n\r\n/**\r\n * Authentication method\r\n */\r\nexport enum AuthMethod {\r\n  /**\r\n   * OAuth 2.0 Authorization Code flow\r\n   */\r\n  AUTHORIZATION_CODE = 'authorization_code',\r\n\r\n  /**\r\n   * Refresh token flow\r\n   */\r\n  REFRESH_TOKEN = 'refresh_token',\r\n}\r\n\r\n/**\r\n * PKCE (Proof Key for Code Exchange) configuration\r\n */\r\nexport interface PKCEConfig {\r\n  /**\r\n   * Code verifier\r\n   */\r\n  code_verifier: string;\r\n\r\n  /**\r\n   * Code challenge\r\n   */\r\n  code_challenge: string;\r\n\r\n  /**\r\n   * Code challenge method\r\n   */\r\n  code_challenge_method: 'S256' | 'plain';\r\n}\r\n\r\n/**\r\n * Authentication URLs\r\n */\r\nexport interface AuthUrls {\r\n  /**\r\n   * Official Account authorization URL\r\n   */\r\n  oa_auth_url: string;\r\n\r\n  /**\r\n   * Social API authorization URL\r\n   */\r\n  social_auth_url: string;\r\n\r\n  /**\r\n   * Token exchange URL\r\n   */\r\n  token_url: string;\r\n\r\n  /**\r\n   * Token refresh URL\r\n   */\r\n  refresh_url: string;\r\n}\r\n\r\n/**\r\n * Official Account authorization result\r\n */\r\nexport interface OAAuthResult {\r\n  /**\r\n   * Authorization URL\r\n   */\r\n  url: string;\r\n\r\n  /**\r\n   * State parameter used (auto-generated if not provided)\r\n   */\r\n  state: string;\r\n\r\n  /**\r\n   * PKCE configuration used (if PKCE was enabled)\r\n   */\r\n  pkce?: PKCEConfig;\r\n}\r\n","/**\r\n * Official Account (OA) related types and interfaces\r\n */\r\n\r\n/**\r\n * Official Account information\r\n */\r\nexport interface OAInfo {\r\n  /**\r\n   * Official Account ID\r\n   */\r\n  oa_id: string;\r\n\r\n  /**\r\n   * OA name\r\n   */\r\n  name: string;\r\n\r\n  /**\r\n   * OA description\r\n   */\r\n  description: string;\r\n\r\n  /**\r\n   * OA alias\r\n   */\r\n  oa_alias: string;\r\n\r\n  /**\r\n   * Verification status\r\n   */\r\n  is_verified: boolean;\r\n\r\n  /**\r\n   * OA type (number)\r\n   */\r\n  oa_type: number;\r\n\r\n  /**\r\n   * Category name\r\n   */\r\n  cate_name: string;\r\n\r\n  /**\r\n   * Number of followers\r\n   */\r\n  num_follower: number;\r\n\r\n  /**\r\n   * Avatar URL\r\n   */\r\n  avatar: string;\r\n\r\n  /**\r\n   * Cover image URL\r\n   */\r\n  cover: string;\r\n\r\n  /**\r\n   * Package name\r\n   */\r\n  package_name: string;\r\n\r\n  /**\r\n   * Package expiration date\r\n   */\r\n  package_valid_through_date: string;\r\n\r\n  /**\r\n   * Package auto-renewal date\r\n   */\r\n  package_auto_renew_date: string;\r\n\r\n  /**\r\n   * Linked Zalo Cloud Account\r\n   */\r\n  linked_ZCA: string;\r\n}\r\n\r\n/**\r\n * Message quota information\r\n */\r\nexport interface MessageQuota {\r\n  /**\r\n   * Daily quota limit\r\n   */\r\n  daily_quota: number;\r\n\r\n  /**\r\n   * Remaining quota for today\r\n   */\r\n  remaining_quota: number;\r\n\r\n  /**\r\n   * Quota type (e.g., \"free\", \"paid\")\r\n   */\r\n  quota_type: string;\r\n\r\n  /**\r\n   * Quota reset time (Unix timestamp)\r\n   */\r\n  reset_time: number;\r\n}\r\n\r\n/**\r\n * Detailed quota information request\r\n */\r\nexport interface QuotaMessageRequest {\r\n  /**\r\n   * Quota owner (OA or APP)\r\n   */\r\n  quota_owner: string;\r\n\r\n  /**\r\n   * Product type\r\n   */\r\n  product_type?: 'cs' | 'transaction' | 'gmf10' | 'gmf50' | 'gmf100';\r\n\r\n  /**\r\n   * Quota type\r\n   */\r\n  quota_type?: 'sub_quota' | 'purchase_quota' | 'reward_quota';\r\n}\r\n\r\n/**\r\n * Quota asset information\r\n */\r\nexport interface QuotaAsset {\r\n  /**\r\n   * Asset ID\r\n   */\r\n  asset_id: string;\r\n\r\n  /**\r\n   * Product type\r\n   */\r\n  product_type: string;\r\n\r\n  /**\r\n   * Quota type\r\n   */\r\n  quota_type: string;\r\n\r\n  /**\r\n   * Expiration date\r\n   */\r\n  valid_through: string;\r\n\r\n  /**\r\n   * Auto-renewal status\r\n   */\r\n  auto_renew: boolean;\r\n\r\n  /**\r\n   * Asset status\r\n   */\r\n  status: 'available' | 'used';\r\n\r\n  /**\r\n   * Used ID (for GMF, this is group_id)\r\n   */\r\n  used_id: string | null;\r\n\r\n  /**\r\n   * Total quota (legacy)\r\n   */\r\n  total?: number;\r\n\r\n  /**\r\n   * Remaining quota (legacy)\r\n   */\r\n  remain?: number;\r\n}\r\n\r\n/**\r\n * Quota message response\r\n */\r\nexport interface QuotaMessageResponse {\r\n  /**\r\n   * List of quota assets\r\n   */\r\n  data: QuotaAsset[];\r\n\r\n  /**\r\n   * Error code\r\n   */\r\n  error: number;\r\n\r\n  /**\r\n   * Response message\r\n   */\r\n  message: string;\r\n}\r\n\r\n/**\r\n * GMF product types\r\n */\r\nexport enum GMFProductType {\r\n  GMF10 = 'gmf10',\r\n  GMF50 = 'gmf50',\r\n  GMF100 = 'gmf100',\r\n}\r\n\r\n/**\r\n * Quota types\r\n */\r\nexport enum QuotaType {\r\n  SUB_QUOTA = 'sub_quota',\r\n  PURCHASE_QUOTA = 'purchase_quota',\r\n  REWARD_QUOTA = 'reward_quota',\r\n}\r\n\r\n/**\r\n * OA settings\r\n */\r\nexport interface OASettings {\r\n  /**\r\n   * Auto-reply settings\r\n   */\r\n  auto_reply?: {\r\n    enabled: boolean;\r\n    message?: string;\r\n  };\r\n\r\n  /**\r\n   * Welcome message settings\r\n   */\r\n  welcome_message?: {\r\n    enabled: boolean;\r\n    message?: string;\r\n  };\r\n\r\n  /**\r\n   * Business hours\r\n   */\r\n  business_hours?: {\r\n    enabled: boolean;\r\n    schedule?: Array<{\r\n      day: number; // 0-6 (Sunday-Saturday)\r\n      start_time: string; // HH:mm format\r\n      end_time: string; // HH:mm format\r\n    }>;\r\n  };\r\n}\r\n\r\n/**\r\n * OA statistics\r\n */\r\nexport interface OAStatistics {\r\n  /**\r\n   * Total followers\r\n   */\r\n  total_followers: number;\r\n\r\n  /**\r\n   * New followers today\r\n   */\r\n  new_followers_today: number;\r\n\r\n  /**\r\n   * Messages sent today\r\n   */\r\n  messages_sent_today: number;\r\n\r\n  /**\r\n   * Messages received today\r\n   */\r\n  messages_received_today: number;\r\n\r\n  /**\r\n   * Engagement rate\r\n   */\r\n  engagement_rate?: number;\r\n}\r\n\r\n/**\r\n * OA profile update request\r\n */\r\nexport interface UpdateOAProfileRequest {\r\n  /**\r\n   * OA name\r\n   */\r\n  name?: string;\r\n\r\n  /**\r\n   * OA description\r\n   */\r\n  description?: string;\r\n\r\n  /**\r\n   * Avatar image (base64 or URL)\r\n   */\r\n  avatar?: string;\r\n\r\n  /**\r\n   * Cover image (base64 or URL)\r\n   */\r\n  cover?: string;\r\n}\r\n\r\n/**\r\n * OA verification request\r\n */\r\nexport interface OAVerificationRequest {\r\n  /**\r\n   * Business license number\r\n   */\r\n  business_license: string;\r\n\r\n  /**\r\n   * Business name\r\n   */\r\n  business_name: string;\r\n\r\n  /**\r\n   * Contact person name\r\n   */\r\n  contact_name: string;\r\n\r\n  /**\r\n   * Contact phone number\r\n   */\r\n  contact_phone: string;\r\n\r\n  /**\r\n   * Contact email\r\n   */\r\n  contact_email: string;\r\n\r\n  /**\r\n   * Additional documents\r\n   */\r\n  documents?: Array<{\r\n    type: string;\r\n    url: string;\r\n  }>;\r\n}\r\n","/**\r\n * User management related types and interfaces\r\n */\r\n\r\n/**\r\n * User information from Zalo API v3.0\r\n */\r\nexport interface UserInfo {\r\n  /**\r\n   * User ID\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * User ID by app\r\n   */\r\n  user_id_by_app: string;\r\n\r\n  /**\r\n   * External user ID in business system\r\n   */\r\n  user_external_id: string;\r\n\r\n  /**\r\n   * Display name\r\n   */\r\n  display_name: string;\r\n\r\n  /**\r\n   * User alias\r\n   */\r\n  user_alias: string;\r\n\r\n  /**\r\n   * Whether user is under 18\r\n   */\r\n  is_sensitive: boolean;\r\n\r\n  /**\r\n   * Last interaction date (dd/MM/yyyy)\r\n   */\r\n  user_last_interaction_date: string;\r\n\r\n  /**\r\n   * Whether user is following OA\r\n   */\r\n  user_is_follower: boolean;\r\n\r\n  /**\r\n   * Avatar URL\r\n   */\r\n  avatar: string;\r\n\r\n  /**\r\n   * Avatar URLs with different sizes\r\n   */\r\n  avatars: {\r\n    \"120\": string;\r\n    \"240\": string;\r\n  };\r\n\r\n  /**\r\n   * Dynamic param from last access\r\n   */\r\n  dynamic_param?: string;\r\n\r\n  /**\r\n   * Tags and notes information\r\n   */\r\n  tags_and_notes_info: {\r\n    notes: string[];\r\n    tag_names: string[];\r\n  };\r\n\r\n  /**\r\n   * Shared information from user\r\n   */\r\n  shared_info?: {\r\n    address?: string;\r\n    city?: string;\r\n    district?: string;\r\n    phone?: string;\r\n    name?: string;\r\n    user_dob?: string;\r\n  };\r\n}\r\n\r\n/**\r\n * User list request parameters\r\n */\r\nexport interface UserListRequest {\r\n  /**\r\n   * Offset for pagination\r\n   */\r\n  offset: number;\r\n\r\n  /**\r\n   * Number of users to fetch (max 50)\r\n   */\r\n  count: number;\r\n\r\n  /**\r\n   * Filter by tag name\r\n   */\r\n  tag_name?: string;\r\n\r\n  /**\r\n   * Filter by last interaction period\r\n   */\r\n  last_interaction_period?: \"TODAY\" | \"YESTERDAY\" | \"L7D\" | \"L30D\" | string;\r\n\r\n  /**\r\n   * Filter by follower status\r\n   */\r\n  is_follower?: boolean;\r\n}\r\n\r\n/**\r\n * User list response\r\n */\r\nexport interface UserListResponse {\r\n  /**\r\n   * Total number of users\r\n   */\r\n  total: number;\r\n\r\n  /**\r\n   * Number of users returned\r\n   */\r\n  count: number;\r\n\r\n  /**\r\n   * Current offset\r\n   */\r\n  offset: number;\r\n\r\n  /**\r\n   * List of user IDs\r\n   */\r\n  users: Array<{\r\n    user_id: string;\r\n  }>;\r\n}\r\n\r\n/**\r\n * User label/tag information\r\n */\r\nexport interface UserLabel {\r\n  /**\r\n   * Label ID\r\n   */\r\n  label_id: string;\r\n\r\n  /**\r\n   * Label name\r\n   */\r\n  label_name: string;\r\n\r\n  /**\r\n   * Label description\r\n   */\r\n  description?: string;\r\n\r\n  /**\r\n   * Label color (hex)\r\n   */\r\n  color?: string;\r\n\r\n  /**\r\n   * Creation time (Unix timestamp)\r\n   */\r\n  created_time?: number;\r\n\r\n  /**\r\n   * Number of users with this label\r\n   */\r\n  user_count?: number;\r\n}\r\n\r\n/**\r\n * Create label request\r\n */\r\nexport interface CreateLabelRequest {\r\n  /**\r\n   * Label name\r\n   */\r\n  label_name: string;\r\n\r\n  /**\r\n   * Label description\r\n   */\r\n  description?: string;\r\n\r\n  /**\r\n   * Label color (hex)\r\n   */\r\n  color?: string;\r\n}\r\n\r\n/**\r\n * User label operation request (legacy - for label_id based operations)\r\n */\r\nexport interface UserLabelRequest {\r\n  /**\r\n   * User ID\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * Label ID\r\n   */\r\n  label_id: string;\r\n}\r\n\r\n/**\r\n * Tag user request - for Zalo API v2.0/oa/tag/tagfollower\r\n */\r\nexport interface TagUserRequest {\r\n  /**\r\n   * User ID (long in API spec, but sent as string)\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * Tag name to assign to user\r\n   * Note: If tag doesn't exist, API will create it automatically\r\n   */\r\n  tag_name: string;\r\n}\r\n\r\n/**\r\n * Remove tag from user request - for Zalo API v2.0/oa/tag/rmfollowerfromtag\r\n */\r\nexport interface RemoveTagFromUserRequest {\r\n  /**\r\n   * User ID\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * Tag name to remove from user\r\n   */\r\n  tag_name: string;\r\n}\r\n\r\n/**\r\n * Get tags list response - for Zalo API v2.0/oa/tag/gettagsofoa\r\n */\r\nexport interface GetTagsResponse {\r\n  /**\r\n   * Error code (0 = success)\r\n   */\r\n  error: number;\r\n\r\n  /**\r\n   * Response message\r\n   */\r\n  message: string;\r\n\r\n  /**\r\n   * Array of tag names\r\n   */\r\n  data: string[];\r\n}\r\n\r\n/**\r\n * Delete tag request - for Zalo API v2.0/oa/tag/rmtag\r\n */\r\nexport interface DeleteTagRequest {\r\n  /**\r\n   * Tag name to delete\r\n   */\r\n  tag_name: string;\r\n}\r\n\r\n/**\r\n * Update user request - for Zalo API v3.0/oa/user/update\r\n */\r\nexport interface UpdateUserRequest {\r\n  /**\r\n   * User ID (required)\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * Shared information to update\r\n   */\r\n  shared_info?: {\r\n    /**\r\n     * Display name\r\n     */\r\n    name?: string;\r\n\r\n    /**\r\n     * Phone number\r\n     */\r\n    phone?: string;\r\n\r\n    /**\r\n     * Address\r\n     */\r\n    address?: string;\r\n\r\n    /**\r\n     * City ID (see Zalo documentation for city codes)\r\n     */\r\n    city_id?: number;\r\n\r\n    /**\r\n     * District ID (see Zalo documentation for district codes)\r\n     */\r\n    district_id?: number;\r\n\r\n    /**\r\n     * Date of birth (dd/MM/yyyy format, from 1/1/1970)\r\n     */\r\n    user_dob?: string;\r\n  };\r\n\r\n  /**\r\n   * User alias name\r\n   */\r\n  user_alias?: string;\r\n\r\n  /**\r\n   * External user ID in business system (unique per customer)\r\n   */\r\n  user_external_id?: string;\r\n}\r\n\r\n/**\r\n * Delete user info request - for Zalo API v2.0/oa/deletefollowerinfo\r\n */\r\nexport interface DeleteUserInfoRequest {\r\n  /**\r\n   * User ID to delete info for\r\n   */\r\n  user_id: string;\r\n}\r\n\r\n/**\r\n * Get user custom info request - for Zalo API v3.0/oa/user/detail/custominfo\r\n */\r\nexport interface GetUserCustomInfoRequest {\r\n  /**\r\n   * User ID to get custom info for\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * List of custom fields to export (optional)\r\n   * If not specified, API returns all available custom info\r\n   * For table type fields, only parent field is accepted\r\n   */\r\n  fields_to_export?: string[];\r\n}\r\n\r\n/**\r\n * User custom info response - for Zalo API v3.0/oa/user/detail/custominfo\r\n */\r\nexport interface UserCustomInfoResponse {\r\n  /**\r\n   * Error code (0 = success)\r\n   */\r\n  error: number;\r\n\r\n  /**\r\n   * Response message\r\n   */\r\n  message: string;\r\n\r\n  /**\r\n   * Custom info data\r\n   */\r\n  data: {\r\n    /**\r\n     * User ID\r\n     */\r\n    user_id: string;\r\n\r\n    /**\r\n     * Custom information fields\r\n     * All values are returned as strings for consistency\r\n     */\r\n    custom_info: Record<string, any>;\r\n  };\r\n}\r\n\r\n/**\r\n * Update user custom info request - for Zalo API v3.0/oa/user/update/custominfo\r\n */\r\nexport interface UpdateUserCustomInfoRequest {\r\n  /**\r\n   * User ID to update custom info for\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * Custom information to update\r\n   * Structure depends on OA's custom field configuration\r\n   */\r\n  custom_info: Record<string, any>;\r\n}\r\n\r\n/**\r\n * User custom information\r\n */\r\nexport interface UserCustomInfo {\r\n  /**\r\n   * User ID\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * Custom fields (key-value pairs)\r\n   */\r\n  custom_fields: Record<string, any>;\r\n\r\n  /**\r\n   * Last updated timestamp\r\n   */\r\n  last_updated?: number;\r\n}\r\n\r\n/**\r\n * Update custom info request\r\n */\r\nexport interface UpdateCustomInfoRequest {\r\n  /**\r\n   * User ID\r\n   */\r\n  user_id: string;\r\n\r\n  /**\r\n   * Custom fields to update\r\n   */\r\n  custom_fields: Record<string, any>;\r\n}\r\n\r\n/**\r\n * User info field types\r\n */\r\nexport enum UserInfoFieldType {\r\n  TEXT = \"text\",\r\n  NUMBER = \"number\",\r\n  DATE = \"date\",\r\n  SELECT = \"select\",\r\n}\r\n\r\n/**\r\n * Field option for select type\r\n */\r\nexport interface FieldOption {\r\n  /**\r\n   * Option value\r\n   */\r\n  value: string;\r\n\r\n  /**\r\n   * Option label\r\n   */\r\n  label: string;\r\n}\r\n\r\n/**\r\n * User info field definition\r\n */\r\nexport interface UserInfoField {\r\n  /**\r\n   * Field ID\r\n   */\r\n  field_id: string;\r\n\r\n  /**\r\n   * Field name\r\n   */\r\n  field_name: string;\r\n\r\n  /**\r\n   * Field type\r\n   */\r\n  field_type: UserInfoFieldType;\r\n\r\n  /**\r\n   * Field description\r\n   */\r\n  description?: string;\r\n\r\n  /**\r\n   * Whether field is required\r\n   */\r\n  required?: boolean;\r\n\r\n  /**\r\n   * Options for select type\r\n   */\r\n  options?: FieldOption[];\r\n\r\n  /**\r\n   * Default value\r\n   */\r\n  default_value?: string;\r\n\r\n  /**\r\n   * Display order\r\n   */\r\n  display_order?: number;\r\n\r\n  /**\r\n   * Whether it's a system field\r\n   */\r\n  is_system_field?: boolean;\r\n\r\n  /**\r\n   * Creation time\r\n   */\r\n  created_time?: number;\r\n\r\n  /**\r\n   * Last update time\r\n   */\r\n  updated_time?: number;\r\n}\r\n\r\n/**\r\n * Create user info field request\r\n */\r\nexport interface CreateUserInfoFieldRequest {\r\n  /**\r\n   * Field name\r\n   */\r\n  field_name: string;\r\n\r\n  /**\r\n   * Field type\r\n   */\r\n  field_type: UserInfoFieldType;\r\n\r\n  /**\r\n   * Field description\r\n   */\r\n  description?: string;\r\n\r\n  /**\r\n   * Whether field is required\r\n   */\r\n  required?: boolean;\r\n\r\n  /**\r\n   * Options for select type\r\n   */\r\n  options?: FieldOption[];\r\n\r\n  /**\r\n   * Default value\r\n   */\r\n  default_value?: string;\r\n\r\n  /**\r\n   * Display order\r\n   */\r\n  display_order?: number;\r\n}\r\n\r\n/**\r\n * Update user info field request\r\n */\r\nexport interface UpdateUserInfoFieldRequest {\r\n  /**\r\n   * Field name\r\n   */\r\n  field_name?: string;\r\n\r\n  /**\r\n   * Field description\r\n   */\r\n  description?: string;\r\n\r\n  /**\r\n   * Whether field is required\r\n   */\r\n  required?: boolean;\r\n\r\n  /**\r\n   * Options for select type\r\n   */\r\n  options?: FieldOption[];\r\n\r\n  /**\r\n   * Default value\r\n   */\r\n  default_value?: string;\r\n\r\n  /**\r\n   * Display order\r\n   */\r\n  display_order?: number;\r\n}\r\n\r\n/**\r\n * Advanced filter options for getAllUsersWithAdvancedFilters\r\n * Dựa trên các trường trong UserInfo\r\n */\r\nexport interface AdvancedUserFilters {\r\n  /**\r\n   * Filter by specific user IDs (exact match)\r\n   * Chỉ lấy những users có ID trong danh sách này\r\n   */\r\n  userIds?: string[];\r\n\r\n  /**\r\n   * Filter by tag name (existing from basic filters)\r\n   */\r\n  tag_name?: string;\r\n\r\n  /**\r\n   * Filter by last interaction period (existing from basic filters)\r\n   */\r\n  last_interaction_period?: \"TODAY\" | \"YESTERDAY\" | \"L7D\" | \"L30D\" | string;\r\n\r\n  /**\r\n   * Filter by follower status (existing from basic filters)\r\n   */\r\n  is_follower?: boolean;\r\n\r\n  /**\r\n   * Filter by display name (contains search)\r\n   */\r\n  display_name_contains?: string;\r\n\r\n  /**\r\n   * Filter by user alias (contains search)\r\n   */\r\n  user_alias_contains?: string;\r\n\r\n  /**\r\n   * Filter by whether user is sensitive (under 18)\r\n   */\r\n  is_sensitive?: boolean;\r\n\r\n  /**\r\n   * Filter by specific interaction date range\r\n   */\r\n  last_interaction_date_range?: {\r\n    from: string; // dd/MM/yyyy format\r\n    to: string; // dd/MM/yyyy format\r\n  };\r\n\r\n  /**\r\n   * Filter by specific tags (multiple tags - OR operation)\r\n   */\r\n  tag_names?: string[];\r\n\r\n  /**\r\n   * Filter by tags (ALL tags must match - AND operation)\r\n   */\r\n  require_all_tags?: string[];\r\n\r\n  /**\r\n   * Filter by notes content (contains search)\r\n   */\r\n  notes_contains?: string;\r\n\r\n  /**\r\n   * Filter by shared info - city\r\n   */\r\n  city?: string;\r\n\r\n  /**\r\n   * Filter by shared info - district\r\n   */\r\n  district?: string;\r\n\r\n  /**\r\n   * Filter by shared info - phone (contains search)\r\n   */\r\n  phone_contains?: string;\r\n\r\n  /**\r\n   * Filter by shared info - name (contains search)\r\n   */\r\n  name_contains?: string;\r\n\r\n  /**\r\n   * Filter by shared info - address (contains search)\r\n   */\r\n  address_contains?: string;\r\n\r\n  /**\r\n   * Filter by date of birth range\r\n   */\r\n  dob_range?: {\r\n    from: string; // dd/MM/yyyy format\r\n    to: string; // dd/MM/yyyy format\r\n  };\r\n\r\n  /**\r\n   * Filter by age range (calculated from dob)\r\n   */\r\n  age_range?: {\r\n    min: number;\r\n    max: number;\r\n  };\r\n\r\n  /**\r\n   * Filter by external user ID (contains search)\r\n   */\r\n  external_id_contains?: string;\r\n\r\n  /**\r\n   * Exclude users with specific tags\r\n   */\r\n  exclude_tags?: string[];\r\n\r\n  /**\r\n   * Filter by users who have shared info vs those who don't\r\n   */\r\n  has_shared_info?: boolean;\r\n\r\n  /**\r\n   * Filter by users who have phone number\r\n   */\r\n  has_phone?: boolean;\r\n\r\n  /**\r\n   * Filter by users who have address\r\n   */\r\n  has_address?: boolean;\r\n}\r\n","/**\r\n * ZNS (Zalo Notification Service) Type Definitions\r\n */\r\n\r\n// ZNS Template Types\r\nexport enum ZNSTemplateType {\r\n  /** ZNS tùy chỉnh */\r\n  CUSTOM = 1,\r\n  /** ZNS xác thực */\r\n  AUTHENTICATION = 2,\r\n  /** ZNS yêu cầu thanh toán */\r\n  PAYMENT_REQUEST = 3,\r\n  /** ZNS voucher */\r\n  VOUCHER = 4,\r\n  /** ZNS Đánh giá dịch vụ */\r\n  SERVICE_RATING = 5\r\n}\r\n\r\n// ZNS Template Tags\r\nexport enum ZNSTemplateTag {\r\n  /** Transaction */\r\n  TRANSACTION = \"1\",\r\n  /** Customer care */\r\n  CUSTOMER_CARE = \"2\",\r\n  /** Promotion */\r\n  PROMOTION = \"3\"\r\n}\r\n\r\n// ZNS Button Types\r\n// ZNS Button Types\r\nexport enum ZNSButtonType {\r\n  /** Trang doanh nghiệp */\r\n  ENTERPRISE_PAGE = 1,\r\n  /** Gọi điện */\r\n  PHONE = 2,\r\n  /** Trang OA */\r\n  OA_PAGE = 3,\r\n  /** Zalo Mini App */\r\n  ZALO_MINI_APP = 4,\r\n  /** Tải App */\r\n  DOWNLOAD_APP = 5,\r\n  /** Phân phối sản phẩm */\r\n  PRODUCT_DISTRIBUTION = 6,\r\n  /** Web/Zalo Mini App khác */\r\n  OTHER_WEB_APP = 7,\r\n  /** App khác */\r\n  OTHER_APP = 8,\r\n  /** Bài viết OA */\r\n  OA_ARTICLE = 9,\r\n  /** Sao chép */\r\n  COPY = 10,\r\n  /** Thanh toán ngay */\r\n  PAY_NOW = 11,\r\n  /** Xem chi tiết */\r\n  VIEW_DETAILS = 12,\r\n}\r\n\r\n// ZNS Parameter Types\r\nexport enum ZNSParamType {\r\n  /** Tên khách hàng (30) */\r\n  CUSTOMER_NAME = \"1\",\r\n  /** Số điện thoại (15) */\r\n  PHONE_NUMBER = \"2\",\r\n  /** Địa chỉ (200) */\r\n  ADDRESS = \"3\",\r\n  /** Mã số (30) */\r\n  CODE = \"4\",\r\n  /** Nhãn tùy chỉnh (30) */\r\n  CUSTOM_LABEL = \"5\",\r\n  /** Trạng thái giao dịch (30) */\r\n  TRANSACTION_STATUS = \"6\",\r\n  /** Thông tin liên hệ (50) */\r\n  CONTACT_INFO = \"7\",\r\n  /** Giới tính / Danh xưng (5) */\r\n  GENDER_TITLE = \"8\",\r\n  /** Tên sản phẩm / Thương hiệu (200) */\r\n  PRODUCT_BRAND = \"9\",\r\n  /** Số lượng / Số tiền (20) */\r\n  QUANTITY_AMOUNT = \"10\",\r\n  /** Thời gian (20) */\r\n  TIME = \"11\",\r\n  /** OTP (10) */\r\n  OTP = \"12\",\r\n  /** URL (200) */\r\n  URL = \"13\",\r\n  /** Tiền tệ (VNĐ) (12) */\r\n  CURRENCY = \"14\",\r\n  /** Bank transfer note (90) */\r\n  BANK_TRANSFER_NOTE = \"15\"\r\n}\r\n\r\nexport interface ZNSMessage {\r\n  phone: string;\r\n  template_id: string;\r\n  template_data: Record<string, any>;\r\n  tracking_id?: string;\r\n}\r\n\r\nexport interface ZNSHashPhoneMessage {\r\n  phone_hash: string;\r\n  template_id: string;\r\n  template_data: Record<string, any>;\r\n  tracking_id?: string;\r\n}\r\n\r\nexport interface ZNSDevModeMessage {\r\n  phone: string;\r\n  template_id: string;\r\n  template_data: Record<string, any>;\r\n  tracking_id?: string;\r\n  mode: \"development\";\r\n}\r\n\r\nexport interface ZNSRsaMessage {\r\n  phone_rsa: string;\r\n  template_id: string;\r\n  template_data: Record<string, any>;\r\n  tracking_id?: string;\r\n}\r\n\r\nexport interface ZNSJourneyMessage {\r\n  phone: string;\r\n  template_id: string;\r\n  template_data: Record<string, any>;\r\n  tracking_id?: string;\r\n  journey_id: string;\r\n}\r\n\r\n/**\r\n * ZBS/ZNS Template Message by UID\r\n * API: POST https://openapi.zalo.me/v3.0/oa/message/template\r\n * Send template message using user_id instead of phone number\r\n */\r\nexport interface ZNSUidMessage {\r\n  /** ID của người nhận - Định danh người dùng theo OA */\r\n  user_id: string;\r\n  /** ID của template muốn sử dụng */\r\n  template_id: string;\r\n  /** Các thuộc tính của template mà đối tác đã đăng ký với Zalo */\r\n  template_data: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Response from sending ZBS/ZNS template message by UID\r\n */\r\nexport interface ZNSUidSendResult {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    /** ID của tin nhắn */\r\n    message_id: string;\r\n    /** ID của người dùng */\r\n    user_id: string;\r\n  };\r\n  /** Thời gian gửi tin (timestamp) */\r\n  sent_time?: string;\r\n}\r\n\r\nexport interface ZNSSendResult {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    msg_id: string;\r\n    sent_time: string;\r\n    quota?: {\r\n      remainingQuota: number;\r\n      dailyQuota: number;\r\n    };\r\n  };\r\n}\r\n\r\nexport interface ZNSTemplate {\r\n  templateId: string;\r\n  templateName: string;\r\n  status: \"PENDING_REVIEW\" | \"ENABLE\" | \"REJECT\" | \"DISABLE\";\r\n  listParams: Array<{\r\n    name: string;\r\n    require: boolean;\r\n    type: \"STRING\" | \"NUMBER\" | \"DATE\";\r\n    maxLength?: number;\r\n    minLength?: number;\r\n  }>;\r\n  timeout?: number;\r\n  previewUrl?: string;\r\n  templateQuality?: \"HIGH\" | \"MEDIUM\" | \"LOW\";\r\n  templateTag?: string;\r\n  price?: number;\r\n  applyTemplateQuota?: boolean;\r\n  templateDailyQuota?: number;\r\n  templateRemainingQuota?: number;\r\n}\r\n\r\nexport interface ZNSTemplateDetails {\r\n  error: number;\r\n  message: string;\r\n  data: {\r\n    templateId: string; // ID của template\r\n    templateName: string; // Tên của template\r\n    status: \"ENABLE\" | \"PENDING_REVIEW\" | \"DELETE\" | \"REJECT\" | \"DISABLE\"; // Trạng thái template\r\n    reason?: string; // Lý do template có trạng thái hiện tại\r\n    listParams: Array<{\r\n      name: string; // Tên thuộc tính\r\n      require: boolean; // Tính bắt buộc của thuộc tính\r\n      type: string; // Định dạng validate của thuộc tính\r\n      maxLength: number; // Số ký tự tối đa\r\n      minLength: number; // Số ký tự tối thiểu\r\n      acceptNull: boolean; // Thuộc tính có thể nhận giá trị rỗng hay không\r\n    }>;\r\n    listButtons: Array<{\r\n      type: number; // Button type (1-12)\r\n      title: string; // Nội dung Button\r\n      content: string; // Đường dẫn liên kết/số điện thoại\r\n    }>;\r\n    timeout: number; // Thời gian timeout của template\r\n    previewUrl: string; // Đường dẫn đến bản xem trước của template\r\n    templateQuality: string; // Chất lượng template (null từ 10/12)\r\n    templateTag: \"TRANSACTION\" | \"CUSTOMER_CARE\" | \"PROMOTION\"; // Loại nội dung template\r\n    price_sdt: string; // Đơn giá gửi tin qua SĐT\r\n    price_uid: string; // Đơn giá gửi tin qua UID\r\n    price: string; // [DEPRECATED] Đơn giá gửi tin qua SĐT (sử dụng price_sdt thay thế)\r\n  };\r\n}\r\n\r\nexport interface ZNSTemplateList {\r\n  error: number;\r\n  message: string;\r\n  data: Array<{\r\n    templateId: string; // ID của template\r\n    templateName: string; // Tên của template\r\n    createdTime: number; // Thời gian tạo template\r\n    status: \"PENDING_REVIEW\" | \"DISABLE\" | \"ENABLE\" | \"REJECT\"; // Trạng thái template\r\n    templateQuality: \"HIGH\" | \"MEDIUM\" | \"LOW\" | \"UNDEFINED\"; // Chất lượng gửi tin\r\n  }>;\r\n  metadata: {\r\n    total: number; // Tổng số lượng template\r\n  };\r\n}\r\n\r\n/**\r\n * ZNS Template Create Request - theo chuẩn Zalo API\r\n * API: POST https://business.openapi.zalo.me/template/create\r\n */\r\nexport interface ZNSCreateTemplateRequest {\r\n  /** Tên mẫu tin (10-60 ký tự) */\r\n  template_name: string;\r\n\r\n  /** Loại mẫu tin */\r\n  template_type: 1 | 2 | 3 | 4 | 5; // 1: ZNS tùy chỉnh, 2: ZNS xác thực, 3: ZNS yêu cầu thanh toán, 4: ZNS voucher, 5: ZNS Đánh giá dịch vụ\r\n\r\n  /** Tag mẫu tin */\r\n  tag: \"1\" | \"2\" | \"3\"; // 1: Transaction, 2: Customer care, 3: Promotion\r\n\r\n  /** Layout của template */\r\n  layout: ZNSTemplateLayout;\r\n\r\n  /** Thông tin về param (optional) */\r\n  params?: ZNSTemplateParam[];\r\n\r\n  /** Ghi chú kiểm duyệt (1-400 ký tự, optional) */\r\n  note?: string;\r\n\r\n  /** Mã tracking do đối tác tự định nghĩa */\r\n  tracking_id: string;\r\n}\r\n\r\n/**\r\n * ZNS Template Edit Request - theo chuẩn Zalo API\r\n * API: POST https://business.openapi.zalo.me/template/edit\r\n * Chỉ có thể chỉnh sửa template có trạng thái REJECT\r\n */\r\nexport interface ZNSUpdateTemplateRequest {\r\n  /** Template ID được hệ thống tự tạo */\r\n  template_id: string;\r\n\r\n  /** Tên mẫu tin (10-60 ký tự) */\r\n  template_name: string;\r\n\r\n  /** Loại mẫu tin */\r\n  template_type: 1 | 2 | 3 | 4 | 5; // 1: ZNS tùy chỉnh, 2: ZNS xác thực, 3: ZNS yêu cầu thanh toán, 4: ZNS voucher, 5: ZNS Đánh giá dịch vụ\r\n\r\n  /** Tag mẫu tin */\r\n  tag: \"1\" | \"2\" | \"3\"; // 1: Transaction, 2: Customer care, 3: Promotion\r\n\r\n  /** Layout của template */\r\n  layout: ZNSTemplateLayout;\r\n\r\n  /** Thông tin về param (optional) */\r\n  params?: ZNSTemplateParam[];\r\n\r\n  /** Ghi chú kiểm duyệt (1-400 ký tự, optional) */\r\n  note?: string;\r\n\r\n  /** Mã tracking do đối tác tự định nghĩa */\r\n  tracking_id: string;\r\n}\r\n\r\n/**\r\n * Layout của ZNS Template\r\n */\r\nexport interface ZNSTemplateLayout {\r\n  /** Header components (optional) */\r\n  header?: {\r\n    components: ZNSValidationComponent[];\r\n  };\r\n\r\n  /** Body components (required) */\r\n  body: {\r\n    components: ZNSValidationComponent[];\r\n  };\r\n\r\n  /** Footer components (optional) */\r\n  footer?: {\r\n    components: ZNSValidationComponent[];\r\n  };\r\n}\r\n\r\n\r\n\r\n/**\r\n * Template parameter theo chuẩn Zalo API\r\n */\r\nexport interface ZNSTemplateParam {\r\n  /** Loại param */\r\n  type: \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\"; // Xem danh sách param type trong docs\r\n\r\n  /** Tên của param */\r\n  name: string;\r\n\r\n  /** Dữ liệu mẫu của param */\r\n  sample_value: string;\r\n}\r\n\r\n/**\r\n * Response từ API create/edit template\r\n */\r\nexport interface ZNSTemplateCreateEditResponse {\r\n  error: number;\r\n  message: string;\r\n  data: {\r\n    /** Template ID */\r\n    template_id: string;\r\n    /** Tên template */\r\n    template_name: string;\r\n    /** Loại template */\r\n    template_type: number;\r\n    /** Trạng thái template sau khi submit */\r\n    status: \"PENDING_REVIEW\";\r\n    /** Template tag */\r\n    tag: number;\r\n    /** App ID của template */\r\n    app_id: string;\r\n    /** OA ID của template */\r\n    oa_id: string;\r\n    /** Đơn giá gửi tin qua SĐT */\r\n    price_sdt: string;\r\n    /** Đơn giá gửi tin qua UID */\r\n    price_uid: string;\r\n    /** Timeout của template */\r\n    timeout: number;\r\n    /** Link preview template */\r\n    preview_url: string;\r\n  };\r\n}\r\n\r\nexport interface ZNSUploadImageResult {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    media_id: string;\r\n  };\r\n}\r\n\r\nexport interface ZNSStatusInfo {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    status: \"PENDING\" | \"SENT\" | \"RECEIVED\" | \"FAILED\";\r\n    sent_time?: string;\r\n    received_time?: string;\r\n    fail_reason?: string;\r\n  };\r\n}\r\n\r\nexport interface ZNSMessageStatusInfo {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    delivery_time: string; // Thời gian thiết bị nhận được thông báo ZNS\r\n    message: string; // Mô tả trạng thái thông báo\r\n    status: number; // Trạng thái: -1 (không tồn tại), 0 (đã gửi lên server), 1 (đã giao đến thiết bị)\r\n  };\r\n}\r\n\r\nexport interface ZNSBatchMessageStatusRequest {\r\n  message_ids: string[]; // Danh sách message IDs cần kiểm tra trạng thái\r\n}\r\n\r\nexport interface ZNSBatchMessageStatusResponse {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    [messageId: string]: {\r\n      delivery_time: string; // Thời gian thiết bị nhận được thông báo ZNS\r\n      message: string; // Mô tả trạng thái thông báo\r\n      status: number; // Trạng thái: -1 (không tồn tại), 0 (đã gửi lên server), 1 (đã giao đến thiết bị)\r\n    };\r\n  };\r\n}\r\n\r\nexport interface ZNSQuotaInfo {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    dailyQuota: string; // Số thông báo ZNS OA được gửi trong 1 ngày\r\n    remainingQuota: string; // Số thông báo ZNS OA được gửi trong ngày còn lại\r\n    dailyQuotaPromotion: string | null; // Số tin ZNS hậu mãi OA được gửi trong ngày (null từ 1/11)\r\n    remainingQuotaPromotion: string | null; // Số tin ZNS hậu mãi còn lại OA được gửi trong ngày (null từ 1/11)\r\n    monthlyPromotionQuota: number; // Số tin ZNS hậu mãi OA được gửi trong tháng\r\n    remainingMonthlyPromotionQuota: number; // Số tin ZNS hậu mãi còn lại OA được gửi trong tháng\r\n    estimatedNextMonthPromotionQuota: number; // Số tin ZNS hậu mãi dự kiến mà OA có thể gửi trong tháng tiếp theo\r\n  };\r\n}\r\n\r\nexport interface ZNSAllowedContentTypes {\r\n  error: number;\r\n  message: string;\r\n  data?: string[]; // Mảng các loại nội dung: [\"TRANSACTION\", \"CUSTOMER_CARE\", \"PROMOTION\"]\r\n}\r\n\r\nexport interface ZNSTemplateSampleData {\r\n  error: number;\r\n  message: string;\r\n  data: Record<string, any>; // Chứa tham số và dữ liệu mẫu của template\r\n}\r\n\r\nexport interface ZNSCustomerRatingResponse {\r\n  error: number;\r\n  data: {\r\n    total: number; // Tổng số lượng đánh giá được trả về\r\n    data: Array<{\r\n      note: string; // Phần ghi chú thêm của khách hàng\r\n      rate: number; // Số sao được khách hàng đánh giá\r\n      submitDate: string; // Thời điểm khách hàng submit đánh giá (timestamp millisecond)\r\n      msgId: string; // ID của thông tin đánh giá\r\n      feedbacks: string[]; // Phần nhận xét từ khách hàng\r\n      trackingId: string; // Tracking ID từ phía đối tác\r\n    }>;\r\n  };\r\n}\r\n\r\nexport interface ZNSOAQualityInfo {\r\n  error: number;\r\n  message: string;\r\n  data: {\r\n    oaCurrentQuality: \"HIGH\" | \"MEDIUM\" | \"LOW\" | \"UNDEFINED\"; // Chất lượng gửi ZNS trong 48 giờ gần nhất\r\n    oa7dayQuality: \"HIGH\" | \"MEDIUM\" | \"LOW\" | \"UNDEFINED\"; // Chất lượng gửi ZNS trong 7 ngày gần nhất\r\n  };\r\n}\r\n\r\nexport interface ZNSQualityHistoryList {\r\n  error: number;\r\n  message: string;\r\n  data?: {\r\n    history: Array<{\r\n      date: string;\r\n      quality: \"HIGH\" | \"MEDIUM\" | \"LOW\";\r\n      qualityScore: number;\r\n      metrics: {\r\n        sent: number;\r\n        delivered: number;\r\n        read: number;\r\n        responded: number;\r\n        complained: number;\r\n      };\r\n    }>;\r\n  };\r\n}\r\n\r\nexport interface ZNSTemplateStatus {\r\n  templateId: string;\r\n  status: \"PENDING_REVIEW\" | \"ENABLE\" | \"REJECT\" | \"DISABLE\";\r\n  rejectReason?: string;\r\n  enabledTime?: string;\r\n  disabledTime?: string;\r\n}\r\n\r\n// ZNS Component Types for Template Creation\r\nexport interface ZNSComponent {\r\n  type: \"HEADER\" | \"BODY\" | \"FOOTER\";\r\n  format?: \"TEXT\" | \"IMAGE\" | \"VIDEO\";\r\n  text?: string;\r\n  url?: string;\r\n  example?: {\r\n    header_text?: string[];\r\n    body_text?: string[][];\r\n    header_handle?: string[];\r\n  };\r\n}\r\n\r\nexport interface ZNSTemplateComponent {\r\n  type: \"TITLE\" | \"PARAGRAPH\" | \"TABLE\" | \"LOGO\" | \"IMAGE\" | \"BUTTONS\";\r\n  content?: string;\r\n  url?: string;\r\n  buttons?: Array<{\r\n    title: string;\r\n    type: \"PHONE_NUMBER\" | \"URL\";\r\n    payload: string;\r\n  }>;\r\n  table?: {\r\n    headers: string[];\r\n    rows: string[][];\r\n  };\r\n}\r\n\r\n// ZNS Error Types\r\nexport interface ZNSError {\r\n  error: number;\r\n  message: string;\r\n  details?: {\r\n    field?: string;\r\n    code?: string;\r\n    description?: string;\r\n  };\r\n}\r\n\r\n// ZNS Analytics Types\r\nexport interface ZNSAnalytics {\r\n  templateId: string;\r\n  period: {\r\n    from: string;\r\n    to: string;\r\n  };\r\n  metrics: {\r\n    sent: number;\r\n    delivered: number;\r\n    read: number;\r\n    clicked: number;\r\n    failed: number;\r\n    deliveryRate: number;\r\n    readRate: number;\r\n    clickRate: number;\r\n  };\r\n  breakdown?: {\r\n    daily?: Array<{\r\n      date: string;\r\n      sent: number;\r\n      delivered: number;\r\n      read: number;\r\n      clicked: number;\r\n      failed: number;\r\n    }>;\r\n    hourly?: Array<{\r\n      hour: number;\r\n      sent: number;\r\n      delivered: number;\r\n      read: number;\r\n      clicked: number;\r\n      failed: number;\r\n    }>;\r\n  };\r\n}\r\n\r\n// ===== ZNS Component Validation Types =====\r\n// Các interface cụ thể cho validation từng component\r\n\r\n/** Attachment object cho LOGO và IMAGES */\r\nexport interface ZNSAttachment {\r\n  type: \"IMAGE\";\r\n  media_id: string;\r\n}\r\n\r\n/** TITLE component */\r\nexport interface ZNSTitleComponent {\r\n  value: string; // 9-65 ký tự, tối đa 4 params\r\n}\r\n\r\n/** PARAGRAPH component */\r\nexport interface ZNSParagraphComponent {\r\n  value: string; // 9-400 ký tự, tối đa 10 params\r\n}\r\n\r\n/** OTP component */\r\nexport interface ZNSOTPComponent {\r\n  value: string; // 1-10 ký tự, tham số cần define type OTP(10)\r\n}\r\n\r\n/** TABLE component */\r\nexport interface ZNSTableComponent {\r\n  rows: ZNSTableRow[]; // 2-8 rows\r\n}\r\n\r\nexport interface ZNSTableRow {\r\n  title: string; // 3-36 ký tự, chỉ text cố định\r\n  value: string; // 3-90 ký tự, có thể có params\r\n  row_type?: 0 | 1 | 2 | 3 | 4 | 5; // Hiệu ứng row\r\n}\r\n\r\n/** LOGO component */\r\nexport interface ZNSLogoComponent {\r\n  light: ZNSAttachment;\r\n  dark: ZNSAttachment;\r\n}\r\n\r\n/** IMAGES component */\r\nexport interface ZNSImagesComponent {\r\n  items: ZNSAttachment[]; // 1-3 attachments\r\n}\r\n\r\n/** BUTTONS component */\r\nexport interface ZNSButtonsComponent {\r\n  items: ZNSButtonItem[]; // 1-2 buttons\r\n}\r\n\r\nexport interface ZNSButtonItem {\r\n  type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; // Button types\r\n  title: string; // 5-30 ký tự, chỉ text cố định\r\n  content: string; // URL/phone, có thể có params (URL(200) sub domain)\r\n}\r\n\r\n/** PAYMENT component */\r\nexport interface ZNSPaymentComponent {\r\n  bank_code: string; // Bank code cố định\r\n  account_name: string; // 1-100 ký tự, text cố định\r\n  bank_account: string; // 1-100 ký tự, text cố định (Doc: bank_account)\r\n  amount: string; // 2,000 - 500,000,000 hoặc param (VNĐ 12)\r\n  note?: string; // 1-90 ký tự, có thể có params (Bank transfer note 90)\r\n}\r\n\r\n/** VOUCHER component */\r\nexport interface ZNSVoucherComponent {\r\n  name: string; // 1-30 ký tự, có thể có params\r\n  condition: string; // 1-40 ký tự, có thể có params\r\n  start_date?: string; // Có thể có params/datetime\r\n  end_date: string; // Có thể có params/datetime\r\n  voucher_code: string; // 1-25 ký tự, có thể có params (Mã số 30)\r\n}\r\n\r\n/** RATING component */\r\nexport interface ZNSRatingComponent {\r\n  items: ZNSRatingItem[]; // Đúng 5 items\r\n}\r\n\r\nexport interface ZNSRatingItem {\r\n  star: 1 | 2 | 3 | 4 | 5; // Số sao\r\n  title: string; // 1-50 ký tự\r\n  question?: string; // 1-100 ký tự\r\n  answers?: string[]; // Danh sách câu trả lời (Doc: answers List string)\r\n  thanks: string; // 1-100 ký tự\r\n  description: string; // 1-200 ký tự\r\n}\r\n\r\n/** Union type cho tất cả các component */\r\nexport type ZNSValidationComponent =\r\n  | { TITLE: ZNSTitleComponent }\r\n  | { PARAGRAPH: ZNSParagraphComponent }\r\n  | { OTP: ZNSOTPComponent }\r\n  | { TABLE: ZNSTableComponent }\r\n  | { LOGO: ZNSLogoComponent }\r\n  | { IMAGES: ZNSImagesComponent }\r\n  | { BUTTONS: ZNSButtonsComponent }\r\n  | { PAYMENT: ZNSPaymentComponent }\r\n  | { VOUCHER: ZNSVoucherComponent }\r\n  | { RATING: ZNSRatingComponent };\r\n","/**\r\n * Types for Zalo Official Account Article Management\r\n * Based on Zalo Article API v2.0\r\n */\r\n\r\nimport { ZaloApiResponse, ZaloResponse } from \"./common\";\r\n\r\n// ==================== ARTICLE COVER TYPES ====================\r\n\r\n/**\r\n * Article cover configuration\r\n */\r\nexport interface ArticleCover {\r\n  /** Cover type: photo or video */\r\n  cover_type: 'photo' | 'video';\r\n  /** Photo URL (required when cover_type is 'photo') */\r\n  photo_url?: string;\r\n  /** Video ID (required when cover_type is 'video') */\r\n  video_id?: string;\r\n  /** Cover view orientation for video */\r\n  cover_view?: 'horizontal' | 'vertical' | 'square';\r\n  /** Cover status */\r\n  status: 'show' | 'hide';\r\n}\r\n\r\n// ==================== ARTICLE BODY TYPES ====================\r\n\r\n/**\r\n * Article body content item\r\n */\r\nexport interface ArticleBodyItem {\r\n  /** Content type */\r\n  type: 'text' | 'image' | 'video' | 'product';\r\n  /** Text content (for type: text) */\r\n  content?: string;\r\n  /** Image URL (for type: image) */\r\n  url?: string;\r\n  /** Video ID (for type: video) */\r\n  video_id?: string;\r\n  /** Product ID (for type: product) */\r\n  id?: string;\r\n  /** Additional properties for video */\r\n  width?: number;\r\n  height?: number;\r\n  thumbnail?: string;\r\n}\r\n\r\n// ==================== ARTICLE REQUEST TYPES ====================\r\n\r\n/**\r\n * Normal article creation request\r\n */\r\nexport interface ArticleNormalRequest {\r\n  type: 'normal';\r\n  title: string;\r\n  author: string;\r\n  cover: ArticleCover;\r\n  description: string;\r\n  body: ArticleBodyItem[];\r\n  related_medias?: string[];\r\n  tracking_link?: string;\r\n  status?: 'show' | 'hide';\r\n  comment?: 'show' | 'hide';\r\n}\r\n\r\n/**\r\n * Video article creation request\r\n */\r\nexport interface ArticleVideoRequest {\r\n  type: 'video';\r\n  title: string;\r\n  description: string;\r\n  video_id: string;\r\n  avatar: string;\r\n  status?: 'show' | 'hide';\r\n  comment?: 'show' | 'hide';\r\n}\r\n\r\n/**\r\n * Article creation request (union type)\r\n */\r\nexport type ArticleRequest = ArticleNormalRequest | ArticleVideoRequest;\r\n\r\n// ==================== ARTICLE RESPONSE TYPES ====================\r\n\r\n/**\r\n * Article creation response\r\n */\r\n// Phản hồi tạo bài viết theo đúng cấu trúc API: { error, message, data: { token } }\r\nexport type ArticleResponse = ZaloResponse<{ token: string }>;\r\n\r\n/**\r\n * Article process check response\r\n */\r\nexport interface ArticleProcessResponse {\r\n  /** Article ID if creation is successful */\r\n  article_id?: string;\r\n  /** Process status */\r\n  status: 'processing' | 'success' | 'failed';\r\n  /** Error message if failed */\r\n  error_message?: string;\r\n  /** Creation timestamp */\r\n  created_time: number;\r\n  /** Last update timestamp */\r\n  updated_time: number;\r\n}\r\n\r\n/**\r\n * Article verification response\r\n */\r\nexport interface ArticleVerifyResponse {\r\n  /** Article ID */\r\n  id: string;\r\n}\r\n\r\n// ==================== ARTICLE DETAIL TYPES ====================\r\n\r\n/**\r\n * Article cite information\r\n */\r\nexport interface ArticleCite {\r\n  url: string;\r\n  description: string;\r\n}\r\n\r\n/**\r\n * Article detail (normal type)\r\n */\r\nexport interface ArticleDetailNormal {\r\n  id: string;\r\n  type: 'normal';\r\n  title: string;\r\n  author: string;\r\n  cover: ArticleCover;\r\n  description: string;\r\n  body: ArticleBodyItem[];\r\n  related_medias: string[];\r\n  tracking_link: string;\r\n  status: 'show' | 'hide';\r\n  comment: 'show' | 'hide';\r\n  cite?: ArticleCite;\r\n  link_view: string;\r\n  total_view: number;\r\n  total_share: number;\r\n  total_like: number;\r\n  total_comment: number;\r\n}\r\n\r\n/**\r\n * Article detail (video type)\r\n */\r\nexport interface ArticleDetailVideo {\r\n  id: string;\r\n  type: 'video';\r\n  title: string;\r\n  description: string;\r\n  video_id: string;\r\n  avatar: string;\r\n  status: 'show' | 'hide';\r\n  comment: 'show' | 'hide';\r\n  link_view: string;\r\n  total_view: number;\r\n  total_share: number;\r\n  total_like: number;\r\n  total_comment: number;\r\n}\r\n\r\n/**\r\n * Article detail (union type)\r\n */\r\nexport type ArticleDetail = ArticleDetailNormal | ArticleDetailVideo;\r\n\r\n// ==================== ARTICLE LIST TYPES ====================\r\n\r\n/**\r\n * Article list request\r\n */\r\nexport interface ArticleListRequest {\r\n  /** Starting position */\r\n  offset: number;\r\n  /** Number of articles to return (max 10) */\r\n  limit: number;\r\n  /** Article type filter */\r\n  type: 'normal' | 'video';\r\n}\r\n\r\n/**\r\n * Article list item - matches Zalo API response structure exactly\r\n */\r\nexport interface ArticleListItem {\r\n  /** Article ID */\r\n  id: string;\r\n  /** Article type */\r\n  type: 'normal' | 'video';\r\n  /** Article title */\r\n  title: string;\r\n  /** Article status */\r\n  status: 'show' | 'hide';\r\n  /** Total view count */\r\n  total_view: number;\r\n  /** Total share count */\r\n  total_share: number;\r\n  /** Article creation date (timestamp) */\r\n  create_date: number;\r\n  /** Article update date (timestamp) */\r\n  update_date: number;\r\n  /** Article thumbnail URL */\r\n  thumb: string;\r\n  /** Article view link */\r\n  link_view: string;\r\n}\r\n\r\n/**\r\n * Article list response data - matches Zalo API response structure exactly\r\n */\r\nexport interface ArticleListData {\r\n  /** Array of articles (called \"medias\" in Zalo API) */\r\n  medias: ArticleListItem[];\r\n  /** Total number of articles */\r\n  total: number;\r\n}\r\n\r\n/**\r\n * Article list response\r\n */\r\nexport type ArticleListResponse = ZaloApiResponse<ArticleListData>;\r\n\r\n// ==================== ARTICLE UPDATE TYPES ====================\r\n\r\n/**\r\n * Normal article update request\r\n */\r\nexport interface ArticleUpdateNormalRequest {\r\n  id: string;\r\n  type: 'normal';\r\n  title: string;\r\n  author: string;\r\n  cover: ArticleCover;\r\n  description: string;\r\n  body: ArticleBodyItem[];\r\n  related_medias?: string[];\r\n  tracking_link?: string;\r\n  status?: 'show' | 'hide';\r\n  comment?: 'show' | 'hide';\r\n}\r\n\r\n/**\r\n * Video article update request\r\n */\r\nexport interface ArticleUpdateVideoRequest {\r\n  id: string;\r\n  type: 'video';\r\n  title: string;\r\n  description: string;\r\n  video_id: string;\r\n  avatar: string;\r\n  status?: 'show' | 'hide';\r\n  comment?: 'show' | 'hide';\r\n}\r\n\r\n/**\r\n * Article update request (union type)\r\n */\r\nexport type ArticleUpdateRequest = ArticleUpdateNormalRequest | ArticleUpdateVideoRequest;\r\n\r\n/**\r\n * Article update response\r\n */\r\nexport interface ArticleUpdateResponse {\r\n  /** Token for tracking article update progress */\r\n  token: string;\r\n}\r\n\r\n// ==================== ARTICLE REMOVAL TYPES ====================\r\n\r\n/**\r\n * Article removal request\r\n */\r\nexport interface ArticleRemoveRequest {\r\n  /** Article ID to remove */\r\n  id: string;\r\n}\r\n\r\n/**\r\n * Article removal response\r\n */\r\nexport interface ArticleRemoveResponse {\r\n  /** Success message */\r\n  message: string;\r\n}\r\n\r\n/**\r\n * Bulk article removal result for individual item\r\n */\r\nexport interface BulkRemoveItemResult {\r\n  /** Article ID that was processed */\r\n  article_id: string;\r\n\r\n  /** Whether the removal was successful */\r\n  success: boolean;\r\n\r\n  /** Success message if removal succeeded */\r\n  message?: string;\r\n\r\n  /** Error message if removal failed */\r\n  error?: string;\r\n\r\n  /** Processing time in milliseconds */\r\n  processing_time?: number;\r\n}\r\n\r\n/**\r\n * Progress information for bulk article removal\r\n */\r\nexport interface BulkRemoveProgress {\r\n  /** Current number of articles processed */\r\n  current_count: number;\r\n\r\n  /** Total number of articles to process */\r\n  total_count: number;\r\n\r\n  /** Progress percentage (0-100) */\r\n  percentage: number;\r\n\r\n  /** Number of successful removals so far */\r\n  successful_count: number;\r\n\r\n  /** Number of failed removals so far */\r\n  failed_count: number;\r\n\r\n  /** Whether the operation is complete */\r\n  is_complete: boolean;\r\n\r\n  /** Current batch being processed */\r\n  current_batch?: number;\r\n\r\n  /** Total number of batches */\r\n  total_batches?: number;\r\n}\r\n\r\n/**\r\n * Options for bulk article removal\r\n */\r\nexport interface BulkRemoveOptions {\r\n  /** Number of articles to process in each batch (default: 10) */\r\n  batch_size?: number;\r\n\r\n  /** Maximum number of concurrent requests (default: 3) */\r\n  max_concurrency?: number;\r\n\r\n  /** Whether to continue processing if some articles fail (default: true) */\r\n  continue_on_error?: boolean;\r\n\r\n  /** Delay between batches in milliseconds (default: 100) */\r\n  batch_delay?: number;\r\n\r\n  /** Timeout for each removal request in milliseconds (default: 10000) */\r\n  request_timeout?: number;\r\n}\r\n\r\n/**\r\n * Complete result for bulk article removal operation\r\n */\r\nexport interface BulkRemoveResult {\r\n  /** Total number of articles processed */\r\n  total_processed: number;\r\n\r\n  /** Number of successful removals */\r\n  successful_count: number;\r\n\r\n  /** Number of failed removals */\r\n  failed_count: number;\r\n\r\n  /** Success rate as percentage (0-100) */\r\n  success_rate: number;\r\n\r\n  /** Total processing time in milliseconds */\r\n  total_time: number;\r\n\r\n  /** Detailed results for each article */\r\n  results: BulkRemoveItemResult[];\r\n\r\n  /** Summary of errors encountered */\r\n  error_summary?: {\r\n    [errorType: string]: number;\r\n  };\r\n}\r\n\r\n// ==================== BULK ARTICLE DETAILS FETCH TYPES ====================\r\n\r\n/**\r\n * Result for individual article detail fetch in bulk operation\r\n */\r\nexport interface BulkDetailItemResult {\r\n  /** Article ID that was processed */\r\n  article_id: string;\r\n\r\n  /** Whether the detail fetch was successful */\r\n  success: boolean;\r\n\r\n  /** Article detail if fetch succeeded */\r\n  detail?: ArticleDetail;\r\n\r\n  /** Error message if fetch failed */\r\n  error?: string;\r\n\r\n  /** Processing time in milliseconds */\r\n  processing_time?: number;\r\n}\r\n\r\n/**\r\n * Progress information for bulk article details fetch\r\n */\r\nexport interface BulkDetailProgress {\r\n  /** Current number of articles processed */\r\n  current_count: number;\r\n\r\n  /** Total number of articles to process */\r\n  total_count: number;\r\n\r\n  /** Progress percentage (0-100) */\r\n  percentage: number;\r\n\r\n  /** Number of successful detail fetches so far */\r\n  successful_count: number;\r\n\r\n  /** Number of failed detail fetches so far */\r\n  failed_count: number;\r\n\r\n  /** Whether the operation is complete */\r\n  is_complete: boolean;\r\n\r\n  /** Current phase of operation */\r\n  phase: 'fetching_list' | 'fetching_details' | 'complete';\r\n\r\n  /** Current batch being processed (for details phase) */\r\n  current_batch?: number;\r\n\r\n  /** Total number of batches (for details phase) */\r\n  total_batches?: number;\r\n}\r\n\r\n/**\r\n * Options for bulk article details fetch\r\n */\r\nexport interface BulkDetailOptions {\r\n  /** Number of articles to fetch details for in each batch (default: 5) */\r\n  detail_batch_size?: number;\r\n\r\n  /** Maximum number of concurrent detail requests (default: 3) */\r\n  max_concurrency?: number;\r\n\r\n  /** Whether to continue processing if some details fail (default: true) */\r\n  continue_on_error?: boolean;\r\n\r\n  /** Delay between detail batches in milliseconds (default: 200) */\r\n  batch_delay?: number;\r\n\r\n  /** Timeout for each detail request in milliseconds (default: 15000) */\r\n  request_timeout?: number;\r\n\r\n  /** Maximum number of articles to process (default: unlimited) */\r\n  max_articles?: number;\r\n\r\n  /** Options for the initial article list fetch */\r\n  list_options?: {\r\n    batch_size?: number;\r\n    max_articles?: number;\r\n  };\r\n}\r\n\r\n/**\r\n * Complete result for bulk article details fetch operation\r\n */\r\nexport interface BulkDetailResult {\r\n  /** Total number of articles processed */\r\n  total_processed: number;\r\n\r\n  /** Number of successful detail fetches */\r\n  successful_count: number;\r\n\r\n  /** Number of failed detail fetches */\r\n  failed_count: number;\r\n\r\n  /** Success rate as percentage (0-100) */\r\n  success_rate: number;\r\n\r\n  /** Total processing time in milliseconds */\r\n  total_time: number;\r\n\r\n  /** Detailed results for each article */\r\n  results: BulkDetailItemResult[];\r\n\r\n  /** Successfully fetched article details */\r\n  articles_with_details: ArticleDetail[];\r\n\r\n  /** Summary of errors encountered */\r\n  error_summary?: {\r\n    [errorType: string]: number;\r\n  };\r\n\r\n  /** Statistics from the initial list fetch */\r\n  list_fetch_stats: {\r\n    total_pages_fetched: number;\r\n    list_fetch_time: number;\r\n  };\r\n}\r\n\r\n// ==================== VIDEO UPLOAD TYPES ====================\r\n\r\n/**\r\n * Video upload response\r\n */\r\nexport interface VideoUploadResponse {\r\n  /** Token for tracking video upload progress */\r\n  token: string;\r\n}\r\n\r\n/**\r\n * Video status response\r\n */\r\nexport interface VideoStatusResponse {\r\n  /** Video ID (available when processing is complete) */\r\n  video_id?: string;\r\n  /** Video name */\r\n  video_name?: string;\r\n  /** Video size in bytes */\r\n  video_size?: number;\r\n  /** Processing status */\r\n  status: number;\r\n  /** Status message */\r\n  status_message: string;\r\n  /** Conversion progress percentage */\r\n  convert_percent: number;\r\n  /** Conversion error code */\r\n  convert_error_code: number;\r\n}\r\n\r\n// ==================== VALIDATION TYPES ====================\r\n\r\n/**\r\n * Article validation error\r\n */\r\nexport interface ArticleValidationError {\r\n  field: string;\r\n  message: string;\r\n  code: string;\r\n}\r\n\r\n/**\r\n * Video file validation constraints\r\n */\r\nexport interface VideoFileConstraints {\r\n  /** Maximum file size in bytes (50MB) */\r\n  maxSize: number;\r\n  /** Allowed file extensions */\r\n  allowedExtensions: string[];\r\n  /** Allowed MIME types */\r\n  allowedMimeTypes: string[];\r\n}\r\n\r\n// ==================== CONSTANTS ====================\r\n\r\n/**\r\n * Article validation constants\r\n */\r\nexport const ARTICLE_CONSTRAINTS = {\r\n  TITLE_MAX_LENGTH: 150,\r\n  AUTHOR_MAX_LENGTH: 50,\r\n  DESCRIPTION_MAX_LENGTH: 300,\r\n  IMAGE_MAX_SIZE: 1024 * 1024, // 1MB\r\n  VIDEO_MAX_SIZE: 50 * 1024 * 1024, // 50MB\r\n  LIST_MAX_LIMIT: 10, // Zalo API maximum limit is 10\r\n} as const;\r\n\r\n/**\r\n * Video upload constraints\r\n */\r\nexport const VIDEO_CONSTRAINTS: VideoFileConstraints = {\r\n  maxSize: 50 * 1024 * 1024, // 50MB\r\n  allowedExtensions: ['.mp4', '.avi'],\r\n  allowedMimeTypes: ['video/mp4', 'video/avi', 'video/x-msvideo'],\r\n} as const;\r\n\r\n/**\r\n * Article status enum\r\n */\r\nexport enum ArticleStatus {\r\n  SHOW = 'show',\r\n  HIDE = 'hide',\r\n}\r\n\r\n/**\r\n * Comment status enum\r\n */\r\nexport enum CommentStatus {\r\n  SHOW = 'show',\r\n  HIDE = 'hide',\r\n}\r\n\r\n/**\r\n * Article type enum\r\n */\r\nexport enum ArticleType {\r\n  NORMAL = 'normal',\r\n  VIDEO = 'video',\r\n}\r\n\r\n/**\r\n * Cover type enum\r\n */\r\nexport enum CoverType {\r\n  PHOTO = 'photo',\r\n  VIDEO = 'video',\r\n}\r\n\r\n/**\r\n * Body item type enum\r\n */\r\nexport enum BodyItemType {\r\n  TEXT = 'text',\r\n  IMAGE = 'image',\r\n  VIDEO = 'video',\r\n  PRODUCT = 'product',\r\n}\r\n\r\n/**\r\n * Video upload status enum\r\n */\r\nexport enum VideoUploadStatus {\r\n  UNKNOWN = 0,           // Trạng thái không xác định\r\n  SUCCESS = 1,           // Video đã được xử lý thành công và có thể sử dụng\r\n  LOCKED = 2,            // Video đã bị khóa\r\n  PROCESSING = 3,        // Video đang được xử lý\r\n  FAILED = 4,            // Video xử lý thất bại\r\n  DELETED = 5,           // Video đã bị xóa\r\n}\r\n","/**\r\n * Broadcast Message Type Definitions for Zalo OA\r\n * API: https://openapi.zalo.me/v2.0/oa/message\r\n */\r\n\r\n/**\r\n * Broadcast recipient target criteria\r\n */\r\nexport interface BroadcastTarget {\r\n  /**\r\n   * Danh sách các nhóm tuổi sẽ nhận thông báo. Các giá trị phân tách nhau bởi dấu \",\"\r\n   * 0: Tuổi từ 0-12\r\n   * 1: Tuổi từ 13-17\r\n   * 2: Tuổi từ 18-24\r\n   * 3: Tuổi từ 25-34\r\n   * 4: Tuổi từ 35-44\r\n   * 5: Tuổi từ 45-54\r\n   * 6: Tuổi từ 55-64\r\n   * 7: Tuổi lớn hơn hay bằng 65\r\n   */\r\n  ages?: string;\r\n\r\n  /**\r\n   * Danh sách nhóm giới tính sẽ nhận thông báo. Các giá trị phân tách nhau bởi dấu \",\"\r\n   * 0: Tất cả các giới tính\r\n   * 1: Nam\r\n   * 2: Nữ\r\n   */\r\n  gender?: string;\r\n\r\n  /**\r\n   * Danh sách các địa điểm sẽ nhận thông báo. Các giá trị phân tách nhau bởi dấu \",\"\r\n   * 0: Miền Bắc Việt Nam\r\n   * 1: Miền Trung Việt Nam\r\n   * 2: Miền Nam Việt Nam\r\n   */\r\n  locations?: string;\r\n\r\n  /**\r\n   * Danh sách các tỉnh, thành phố sẽ nhận thông báo. Các giá trị phân tách nhau bởi dấu \",\"\r\n   * Nếu được thiết lập, thuộc tính này sẽ thay thế thuộc tính \"locations\"\r\n   */\r\n  cities?: string;\r\n\r\n  /**\r\n   * Danh sách các hệ điều hành di động sẽ nhận thông báo. Các giá trị phân tách nhau bởi dấu \",\"\r\n   * 1: IOS\r\n   * 2: Android\r\n   * 3: Window Phone\r\n   */\r\n  platform?: string;\r\n}\r\n\r\n/**\r\n * Broadcast recipient\r\n */\r\nexport interface BroadcastRecipient {\r\n  target: BroadcastTarget;\r\n}\r\n\r\n/**\r\n * Broadcast message attachment payload element\r\n */\r\nexport interface BroadcastMessageElement {\r\n  /**\r\n   * Loại media - bắt buộc phải là \"article\"\r\n   */\r\n  media_type: \"article\";\r\n\r\n  /**\r\n   * ID của bài viết muốn broadcast\r\n   */\r\n  attachment_id: string;\r\n}\r\n\r\n/**\r\n * Broadcast message attachment payload\r\n */\r\nexport interface BroadcastMessagePayload {\r\n  /**\r\n   * Loại template - bắt buộc phải là \"media\"\r\n   */\r\n  template_type: \"media\";\r\n\r\n  /**\r\n   * Chứa các đối tượng của template\r\n   */\r\n  elements: BroadcastMessageElement[];\r\n}\r\n\r\n/**\r\n * Broadcast message attachment\r\n */\r\nexport interface BroadcastMessageAttachment {\r\n  /**\r\n   * Loại attachment - bắt buộc phải là \"template\"\r\n   */\r\n  type: \"template\";\r\n\r\n  /**\r\n   * Chứa payload của attachment muốn gửi\r\n   */\r\n  payload: BroadcastMessagePayload;\r\n}\r\n\r\n/**\r\n * Broadcast message\r\n */\r\nexport interface BroadcastMessage {\r\n  attachment: BroadcastMessageAttachment;\r\n}\r\n\r\n/**\r\n * Broadcast request\r\n */\r\nexport interface BroadcastRequest {\r\n  /**\r\n   * Thông tin người nhận\r\n   */\r\n  recipient: BroadcastRecipient;\r\n\r\n  /**\r\n   * Nội dung và các attachment cần gửi\r\n   */\r\n  message: BroadcastMessage;\r\n}\r\n\r\n/**\r\n * Broadcast response data\r\n */\r\nexport interface BroadcastResponseData {\r\n  /**\r\n   * ID của tin broadcast\r\n   */\r\n  message_id: string;\r\n}\r\n\r\n/**\r\n * Broadcast response\r\n */\r\nexport interface BroadcastResponse {\r\n  /**\r\n   * Dữ liệu phản hồi\r\n   */\r\n  data: BroadcastResponseData;\r\n\r\n  /**\r\n   * Mã lỗi (0 = thành công)\r\n   */\r\n  error: number;\r\n\r\n  /**\r\n   * Thông báo\r\n   */\r\n  message: string;\r\n}\r\n\r\n/**\r\n * City codes mapping for broadcast targeting\r\n */\r\nexport const BROADCAST_CITY_CODES = {\r\n  \"Đồng Tháp\": \"0\",\r\n  \"Bình Phước\": \"1\",\r\n  \"Ninh Bình\": \"2\",\r\n  \"Bạc Liêu\": \"3\",\r\n  \"Hồ Chí Minh\": \"4\",\r\n  \"Vĩnh Long\": \"5\",\r\n  \"Lâm Đồng\": \"6\",\r\n  \"Yên Bái\": \"7\",\r\n  \"Hà Nam\": \"8\",\r\n  \"Hà Nội\": \"9\",\r\n  \"Hải Dương\": \"10\",\r\n  \"Hậu Giang\": \"11\",\r\n  \"An Giang\": \"12\",\r\n  \"Trà Vinh\": \"13\",\r\n  \"Tiền Giang\": \"14\",\r\n  \"Tây Ninh\": \"15\",\r\n  \"Đồng Nai\": \"16\",\r\n  \"Đắk Lắk\": \"17\",\r\n  \"Bình Định\": \"18\",\r\n  \"Kon Tum\": \"19\",\r\n  \"Đà Nẵng\": \"20\",\r\n  \"Bắc Giang\": \"21\",\r\n  \"Bắc Kạn\": \"22\",\r\n  \"Điện Biên\": \"23\",\r\n  \"Hòa Bình\": \"24\",\r\n  \"Thái Bình\": \"25\",\r\n  \"Vĩnh Phúc\": \"26\",\r\n  \"Hà Giang\": \"27\",\r\n  \"Kiên Giang\": \"28\",\r\n  \"Bình Dương\": \"29\",\r\n  \"Bình Thuận\": \"30\",\r\n  \"Đắk Nông\": \"31\",\r\n  \"Khánh Hòa\": \"32\",\r\n  \"Gia Lai\": \"33\",\r\n  \"Quảng Nam\": \"34\",\r\n  \"Quảng Trị\": \"35\",\r\n  \"Hà Tĩnh\": \"36\",\r\n  \"Hưng Yên\": \"37\",\r\n  \"Quảng Ninh\": \"38\",\r\n  \"Thanh Hóa\": \"39\",\r\n  \"Phú Thọ\": \"40\",\r\n  \"Lai Châu\": \"41\",\r\n  \"Thái Nguyên\": \"42\",\r\n  \"Cao Bằng\": \"43\",\r\n  \"Cà Mau\": \"44\",\r\n  \"Cần Thơ\": \"45\",\r\n  \"Sóc Trăng\": \"46\",\r\n  \"Bến Tre\": \"47\",\r\n  \"Long An\": \"48\",\r\n  \"Bà Rịa Vũng Tàu\": \"49\",\r\n  \"Ninh Thuận\": \"50\",\r\n  \"Phú Yên\": \"51\",\r\n  \"Quãng Ngãi\": \"52\",\r\n  \"Thừa Thiên Huế\": \"53\",\r\n  \"Quảng Bình\": \"54\",\r\n  \"Nghệ An\": \"55\",\r\n  \"Nam Định\": \"56\",\r\n  \"Hải Phòng\": \"57\",\r\n  \"Lạng Sơn\": \"58\",\r\n  \"Lào Cai\": \"59\",\r\n  \"Sơn La\": \"60\",\r\n  \"Bắc Ninh\": \"61\",\r\n  \"Tuyên Quang\": \"62\",\r\n  \"Không Thuộc Việt Nam\": \"63\"\r\n} as const;\r\n\r\n/**\r\n * Age group codes for broadcast targeting\r\n */\r\nexport const BROADCAST_AGE_CODES = {\r\n  \"0-12\": \"0\",\r\n  \"13-17\": \"1\", \r\n  \"18-24\": \"2\",\r\n  \"25-34\": \"3\",\r\n  \"35-44\": \"4\",\r\n  \"45-54\": \"5\",\r\n  \"55-64\": \"6\",\r\n  \"65+\": \"7\"\r\n} as const;\r\n\r\n/**\r\n * Gender codes for broadcast targeting\r\n */\r\nexport const BROADCAST_GENDER_CODES = {\r\n  \"ALL\": \"0\",\r\n  \"MALE\": \"1\",\r\n  \"FEMALE\": \"2\"\r\n} as const;\r\n\r\n/**\r\n * Location codes for broadcast targeting\r\n */\r\nexport const BROADCAST_LOCATION_CODES = {\r\n  \"NORTH\": \"0\",\r\n  \"CENTRAL\": \"1\", \r\n  \"SOUTH\": \"2\"\r\n} as const;\r\n\r\n/**\r\n * Platform codes for broadcast targeting\r\n */\r\nexport const BROADCAST_PLATFORM_CODES = {\r\n  \"IOS\": \"1\",\r\n  \"ANDROID\": \"2\",\r\n  \"WINDOWS_PHONE\": \"3\"\r\n} as const;\r\n\r\n/**\r\n * Result for individual broadcast message\r\n */\r\nexport interface BroadcastMessageResult {\r\n  /**\r\n   * Article attachment ID\r\n   */\r\n  attachmentId: string;\r\n\r\n  /**\r\n   * Whether the broadcast was successful\r\n   */\r\n  success: boolean;\r\n\r\n  /**\r\n   * Message ID if successful\r\n   */\r\n  messageId: string | null;\r\n\r\n  /**\r\n   * Error if failed\r\n   */\r\n  error: Error | null;\r\n\r\n  /**\r\n   * Timestamp when sent\r\n   */\r\n  sentAt: Date;\r\n}\r\n\r\n/**\r\n * Progress information for multiple broadcast\r\n */\r\nexport interface MultipleBroadcastProgress {\r\n  /**\r\n   * Total number of messages to send\r\n   */\r\n  total: number;\r\n\r\n  /**\r\n   * Number of messages completed\r\n   */\r\n  completed: number;\r\n\r\n  /**\r\n   * Number of successful messages\r\n   */\r\n  successful: number;\r\n\r\n  /**\r\n   * Number of failed messages\r\n   */\r\n  failed: number;\r\n\r\n  /**\r\n   * Current attachment ID being processed\r\n   */\r\n  currentAttachmentId: string;\r\n\r\n  /**\r\n   * Whether all messages are completed\r\n   */\r\n  isCompleted: boolean;\r\n}\r\n\r\n/**\r\n * Result for multiple broadcast messages\r\n */\r\nexport interface MultipleBroadcastResult {\r\n  /**\r\n   * Total number of messages sent\r\n   */\r\n  totalMessages: number;\r\n\r\n  /**\r\n   * Number of successful messages\r\n   */\r\n  successfulMessages: number;\r\n\r\n  /**\r\n   * Number of failed messages\r\n   */\r\n  failedMessages: number;\r\n\r\n  /**\r\n   * Individual message results\r\n   */\r\n  results: BroadcastMessageResult[];\r\n\r\n  /**\r\n   * Total duration in milliseconds\r\n   */\r\n  totalDuration: number;\r\n\r\n  /**\r\n   * Sending mode used\r\n   */\r\n  mode: 'parallel' | 'sequential';\r\n\r\n  /**\r\n   * Target criteria used\r\n   */\r\n  targetCriteria: BroadcastTarget;\r\n}\r\n","/**\r\n * Purchase API types for Zalo Official Account\r\n * \r\n * API để tạo đơn hàng mua sản phẩm/dịch vụ OA\r\n * Yêu cầu: Ứng dụng cần được cấp quyền quản lý \"Mua sản phẩm dịch vụ OA\"\r\n */\r\n\r\n/**\r\n * Beneficiary types - Đối tượng thụ hưởng\r\n */\r\nexport type BeneficiaryType = \"OA\" | \"APP\";\r\n\r\n/**\r\n * Request để tạo đơn hàng với product_id\r\n */\r\nexport interface CreateOrderWithProductRequest {\r\n  /**\r\n   * Đối tượng được thụ hưởng khi mua đơn hàng\r\n   * - OA: Official Account\r\n   * - APP: Application\r\n   * Lưu ý: Mỗi sản phẩm sẽ quy định đối tượng khả dụng riêng\r\n   */\r\n  beneficiary: BeneficiaryType;\r\n\r\n  /**\r\n   * ID của sản phẩm muốn mua\r\n   * Chỉ sử dụng product_id HOẶC redeem_code, không được dùng cả hai\r\n   */\r\n  product_id: number;\r\n\r\n  /**\r\n   * Mã giảm giá (tùy chọn)\r\n   * VD: Giảm 100K, Giảm 10%, ...\r\n   */\r\n  voucher_code?: string;\r\n}\r\n\r\n/**\r\n * Request để tạo đơn hàng với redeem_code\r\n */\r\nexport interface CreateOrderWithRedeemRequest {\r\n  /**\r\n   * Đối tượng được thụ hưởng khi mua đơn hàng\r\n   */\r\n  beneficiary: BeneficiaryType;\r\n\r\n  /**\r\n   * Mã quà tặng\r\n   * Chỉ sử dụng product_id HOẶC redeem_code, không được dùng cả hai\r\n   */\r\n  redeem_code: string;\r\n\r\n  /**\r\n   * Mã giảm giá (tùy chọn)\r\n   */\r\n  voucher_code?: string;\r\n}\r\n\r\n/**\r\n * Union type cho request tạo đơn hàng\r\n */\r\nexport type CreateOrderRequest = CreateOrderWithProductRequest | CreateOrderWithRedeemRequest;\r\n\r\n/**\r\n * Response data khi tạo đơn hàng thành công\r\n */\r\nexport interface OrderData {\r\n  /**\r\n   * ID đơn hàng\r\n   * Lưu ý: Order chỉ được tạo từ 00:01 đến 23h54\r\n   */\r\n  order_id: string;\r\n\r\n  /**\r\n   * Loại đối tượng thụ hưởng\r\n   */\r\n  beneficiary_type: BeneficiaryType;\r\n\r\n  /**\r\n   * ID của OA (nếu beneficiary_type = OA) hoặc ID của App (nếu beneficiary_type = APP)\r\n   */\r\n  beneficiary_id: number;\r\n\r\n  /**\r\n   * ID sản phẩm\r\n   */\r\n  product_id: number;\r\n\r\n  /**\r\n   * Tên sản phẩm\r\n   */\r\n  product_name: string;\r\n\r\n  /**\r\n   * Tài khoản ZCA thanh toán đơn hàng\r\n   */\r\n  zca_id: number;\r\n\r\n  /**\r\n   * Voucher code đã được sử dụng (nếu có)\r\n   */\r\n  voucher_code?: string;\r\n\r\n  /**\r\n   * Mã quà tặng (nếu có)\r\n   */\r\n  redeem_code?: string;\r\n\r\n  /**\r\n   * Giá trị của voucher_code\r\n   * VD: Giảm 10% hay Giảm 100K, ...\r\n   */\r\n  discount?: number;\r\n\r\n  /**\r\n   * Giá tiền của đơn hàng (chưa áp dụng mã giảm giá)\r\n   */\r\n  amount: number;\r\n\r\n  /**\r\n   * Giá tiền chính thức sau khi đã sử dụng mã giảm giá\r\n   */\r\n  final_amount: number;\r\n\r\n  /**\r\n   * Thời điểm tạo đơn hàng, tính bằng millisecond\r\n   */\r\n  created_time: number;\r\n\r\n  /**\r\n   * OTT - One Time Token\r\n   * Mã xác thực dùng để xác nhận thanh toán đơn hàng\r\n   * Sử dụng tại API xác nhận thanh toán đơn hàng\r\n   * OTT sẽ có hiệu lực trong vòng 5 phút kể từ khi Order được khởi tạo\r\n   */\r\n  verified_token: string;\r\n}\r\n\r\n/**\r\n * Response khi tạo đơn hàng thành công\r\n */\r\nexport interface CreateOrderResponse {\r\n  error: 0;\r\n  message: \"Success\";\r\n  data: OrderData;\r\n}\r\n\r\n/**\r\n * Helper type để kiểm tra request type\r\n */\r\nexport function isProductOrderRequest(\r\n  request: CreateOrderRequest\r\n): request is CreateOrderWithProductRequest {\r\n  return 'product_id' in request;\r\n}\r\n\r\n/**\r\n * Helper type để kiểm tra request type\r\n */\r\nexport function isRedeemOrderRequest(\r\n  request: CreateOrderRequest\r\n): request is CreateOrderWithRedeemRequest {\r\n  return 'redeem_code' in request;\r\n}\r\n\r\n/**\r\n * Validation errors cho Purchase API\r\n */\r\nexport interface PurchaseValidationError {\r\n  field: string;\r\n  message: string;\r\n}\r\n\r\n/**\r\n * Request để xác nhận thanh toán đơn hàng\r\n */\r\nexport interface ConfirmOrderRequest {\r\n  /**\r\n   * ID đơn hàng\r\n   * Lấy từ response của API tạo đơn hàng\r\n   */\r\n  order_id: string;\r\n\r\n  /**\r\n   * OTT - One Time Token\r\n   * Mã xác thực dùng để xác nhận thanh toán đơn hàng\r\n   * Lấy từ response của API tạo đơn hàng\r\n   * OTT sẽ có hiệu lực trong vòng 5 phút kể từ khi Order được khởi tạo\r\n   */\r\n  verified_token: string;\r\n}\r\n\r\n/**\r\n * Response data khi xác nhận thanh toán đơn hàng thành công\r\n * Lưu ý: Response không có verified_token như khi tạo đơn hàng\r\n */\r\nexport interface ConfirmedOrderData {\r\n  /**\r\n   * ID đơn hàng\r\n   */\r\n  order_id: string;\r\n\r\n  /**\r\n   * Loại đối tượng thụ hưởng\r\n   */\r\n  beneficiary_type: BeneficiaryType;\r\n\r\n  /**\r\n   * ID của OA (nếu beneficiary_type = OA) hoặc ID của App (nếu beneficiary_type = APP)\r\n   */\r\n  beneficiary_id: number;\r\n\r\n  /**\r\n   * ID sản phẩm\r\n   */\r\n  product_id: number;\r\n\r\n  /**\r\n   * Tên sản phẩm\r\n   */\r\n  product_name: string;\r\n\r\n  /**\r\n   * Tài khoản ZCA thanh toán đơn hàng\r\n   */\r\n  zca_id: number;\r\n\r\n  /**\r\n   * Voucher code đã được sử dụng (nếu có)\r\n   */\r\n  voucher_code?: string;\r\n\r\n  /**\r\n   * Mã quà tặng (nếu có)\r\n   */\r\n  redeem_code?: string;\r\n\r\n  /**\r\n   * Giá trị của voucher_code\r\n   * VD: Giảm 10% hay Giảm 100K, ...\r\n   */\r\n  discount?: number;\r\n\r\n  /**\r\n   * Giá tiền của đơn hàng (chưa áp dụng mã giảm giá)\r\n   */\r\n  amount: number;\r\n\r\n  /**\r\n   * Giá tiền chính thức sau khi đã sử dụng mã giảm giá\r\n   */\r\n  final_amount: number;\r\n\r\n  /**\r\n   * Thời điểm tạo đơn hàng, tính bằng millisecond\r\n   */\r\n  created_time: number;\r\n}\r\n\r\n/**\r\n * Response khi xác nhận thanh toán đơn hàng thành công\r\n */\r\nexport interface ConfirmOrderResponse {\r\n  error: 0;\r\n  message: \"Success\";\r\n  data: ConfirmedOrderData;\r\n}\r\n\r\n/**\r\n * Danh sách sản phẩm OA có sẵn\r\n */\r\nexport const OA_PRODUCTS = {\r\n  // Sản phẩm OA Subscription\r\n  OA_ADVANCED_6M: 866836109767958135,\r\n  OA_ADVANCED_12M: 1302963828004138542,\r\n  OA_PREMIUM_6M: 757295129578622372,\r\n  OA_PREMIUM_12M: 3071996459978068910,\r\n\r\n  // Sản phẩm Quota khởi tạo GMF\r\n  GMF_10_MEMBERS: 739448264820568793,\r\n  GMF_50_MEMBERS: 2405469629611791306,\r\n  GMF_100_MEMBERS: 2275350247265190619,\r\n  GMF_1000_MEMBERS: 3678557233392100095,\r\n\r\n  // Sản phẩm Gói quota tin nhắn OA\r\n  TRANSACTION_5K: 4609774892111011697,\r\n  TRANSACTION_50K: 2038298593847789538,\r\n  TRANSACTION_500K: 2544831667685731909,\r\n} as const;\r\n\r\n/**\r\n * Thông tin chi tiết sản phẩm\r\n */\r\nexport interface ProductInfo {\r\n  id: number;\r\n  name: string;\r\n  beneficiary: BeneficiaryType[];\r\n  category: 'subscription' | 'gmf' | 'quota';\r\n}\r\n\r\n/**\r\n * Danh sách thông tin chi tiết sản phẩm\r\n */\r\nexport const PRODUCT_INFO: Record<number, ProductInfo> = {\r\n  // OA Subscription\r\n  [OA_PRODUCTS.OA_ADVANCED_6M]: {\r\n    id: OA_PRODUCTS.OA_ADVANCED_6M,\r\n    name: \"Gói OA Nâng cao 6 tháng\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"subscription\"\r\n  },\r\n  [OA_PRODUCTS.OA_ADVANCED_12M]: {\r\n    id: OA_PRODUCTS.OA_ADVANCED_12M,\r\n    name: \"Gói OA Nâng cao 12 tháng\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"subscription\"\r\n  },\r\n  [OA_PRODUCTS.OA_PREMIUM_6M]: {\r\n    id: OA_PRODUCTS.OA_PREMIUM_6M,\r\n    name: \"Gói OA Premium 6 tháng\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"subscription\"\r\n  },\r\n  [OA_PRODUCTS.OA_PREMIUM_12M]: {\r\n    id: OA_PRODUCTS.OA_PREMIUM_12M,\r\n    name: \"Gói OA Premium 12 tháng\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"subscription\"\r\n  },\r\n\r\n  // GMF Products\r\n  [OA_PRODUCTS.GMF_10_MEMBERS]: {\r\n    id: OA_PRODUCTS.GMF_10_MEMBERS,\r\n    name: \"GMF tối đa 10 thành viên\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"gmf\"\r\n  },\r\n  [OA_PRODUCTS.GMF_50_MEMBERS]: {\r\n    id: OA_PRODUCTS.GMF_50_MEMBERS,\r\n    name: \"GMF tối đa 50 thành viên\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"gmf\"\r\n  },\r\n  [OA_PRODUCTS.GMF_100_MEMBERS]: {\r\n    id: OA_PRODUCTS.GMF_100_MEMBERS,\r\n    name: \"GMF tối đa 100 thành viên\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"gmf\"\r\n  },\r\n  [OA_PRODUCTS.GMF_1000_MEMBERS]: {\r\n    id: OA_PRODUCTS.GMF_1000_MEMBERS,\r\n    name: \"GMF tối đa 1000 thành viên\",\r\n    beneficiary: [\"OA\"],\r\n    category: \"gmf\"\r\n  },\r\n\r\n  // Transaction Quota\r\n  [OA_PRODUCTS.TRANSACTION_5K]: {\r\n    id: OA_PRODUCTS.TRANSACTION_5K,\r\n    name: \"Gói 5k tin Giao dịch (1 tháng)\",\r\n    beneficiary: [\"APP\", \"OA\"],\r\n    category: \"quota\"\r\n  },\r\n  [OA_PRODUCTS.TRANSACTION_50K]: {\r\n    id: OA_PRODUCTS.TRANSACTION_50K,\r\n    name: \"Gói 50k tin Giao dịch (1 tháng)\",\r\n    beneficiary: [\"APP\", \"OA\"],\r\n    category: \"quota\"\r\n  },\r\n  [OA_PRODUCTS.TRANSACTION_500K]: {\r\n    id: OA_PRODUCTS.TRANSACTION_500K,\r\n    name: \"Gói 500k tin Giao dịch (1 tháng)\",\r\n    beneficiary: [\"APP\", \"OA\"],\r\n    category: \"quota\"\r\n  },\r\n};\r\n\r\n/**\r\n * Purchase API error codes\r\n */\r\nexport enum PurchaseErrorCode {\r\n  INVALID_BENEFICIARY = 1001,\r\n  INVALID_PRODUCT_ID = 1002,\r\n  INVALID_REDEEM_CODE = 1003,\r\n  INVALID_VOUCHER_CODE = 1004,\r\n  PRODUCT_NOT_AVAILABLE = 1005,\r\n  INSUFFICIENT_BALANCE = 1006,\r\n  ORDER_TIME_RESTRICTED = 1007,\r\n  DUPLICATE_ORDER = 1008,\r\n  PERMISSION_DENIED = 1009,\r\n  INVALID_ORDER_ID = 1010,\r\n  INVALID_VERIFIED_TOKEN = 1011,\r\n  ORDER_EXPIRED = 1012,\r\n  ORDER_ALREADY_CONFIRMED = 1013,\r\n  SYSTEM_ERROR = 9999,\r\n}\r\n","/**\r\n * ZNS Constants - Các hằng số theo chuẩn Zalo API\r\n */\r\n\r\nimport {\r\n  ZNSTemplateType,\r\n  ZNSTemplateTag,\r\n  ZNSButtonType,\r\n  ZNSParamType\r\n} from '../types/zns';\r\n\r\n/**\r\n * Template Type mapping với description\r\n */\r\nexport const ZNS_TEMPLATE_TYPES = {\r\n  [ZNSTemplateType.CUSTOM]: {\r\n    value: ZNSTemplateType.CUSTOM,\r\n    name: 'ZNS tùy chỉnh',\r\n    description: 'Template tùy chỉnh cho các mục đích khác nhau'\r\n  },\r\n  [ZNSTemplateType.AUTHENTICATION]: {\r\n    value: ZNSTemplateType.AUTHENTICATION,\r\n    name: 'ZNS xác thực',\r\n    description: 'Template cho việc xác thực OTP, mã PIN'\r\n  },\r\n  [ZNSTemplateType.PAYMENT_REQUEST]: {\r\n    value: ZNSTemplateType.PAYMENT_REQUEST,\r\n    name: 'ZNS yêu cầu thanh toán',\r\n    description: 'Template yêu cầu thanh toán'\r\n  },\r\n  [ZNSTemplateType.VOUCHER]: {\r\n    value: ZNSTemplateType.VOUCHER,\r\n    name: 'ZNS voucher',\r\n    description: 'Template gửi voucher, khuyến mãi'\r\n  },\r\n  [ZNSTemplateType.SERVICE_RATING]: {\r\n    value: ZNSTemplateType.SERVICE_RATING,\r\n    name: 'ZNS đánh giá dịch vụ',\r\n    description: 'Template yêu cầu đánh giá dịch vụ'\r\n  }\r\n} as const;\r\n\r\n/**\r\n * Template Tag mapping với description\r\n */\r\nexport const ZNS_TEMPLATE_TAGS = {\r\n  [ZNSTemplateTag.TRANSACTION]: {\r\n    value: ZNSTemplateTag.TRANSACTION,\r\n    name: 'Transaction',\r\n    description: 'Giao dịch, thanh toán'\r\n  },\r\n  [ZNSTemplateTag.CUSTOMER_CARE]: {\r\n    value: ZNSTemplateTag.CUSTOMER_CARE,\r\n    name: 'Customer Care',\r\n    description: 'Chăm sóc khách hàng'\r\n  },\r\n  [ZNSTemplateTag.PROMOTION]: {\r\n    value: ZNSTemplateTag.PROMOTION,\r\n    name: 'Promotion',\r\n    description: 'Khuyến mãi, quảng cáo'\r\n  }\r\n} as const;\r\n\r\n/**\r\n * Button Type mapping với description\r\n */\r\nexport const ZNS_BUTTON_TYPES = {\r\n  [ZNSButtonType.ENTERPRISE_PAGE]: {\r\n    value: ZNSButtonType.ENTERPRISE_PAGE,\r\n    name: 'Trang doanh nghiệp',\r\n    description: 'Đến trang của doanh nghiệp hoặc trang tra cứu hoá đơn điện tử'\r\n  },\r\n  [ZNSButtonType.PHONE]: {\r\n    value: ZNSButtonType.PHONE,\r\n    name: 'Phone',\r\n    description: 'Gọi điện thoại'\r\n  },\r\n  [ZNSButtonType.OA_PAGE]: {\r\n    value: ZNSButtonType.OA_PAGE,\r\n    name: 'Trang OA',\r\n    description: 'Đến trang thông tin OA'\r\n  },\r\n  [ZNSButtonType.ZALO_MINI_APP]: {\r\n    value: ZNSButtonType.ZALO_MINI_APP,\r\n    name: 'Zalo Mini App',\r\n    description: 'Đến ứng dụng Zalo Mini App của doanh nghiệp'\r\n  },\r\n  [ZNSButtonType.DOWNLOAD_APP]: {\r\n    value: ZNSButtonType.DOWNLOAD_APP,\r\n    name: 'Tải App',\r\n    description: 'Đến trang tải ứng dụng'\r\n  },\r\n  [ZNSButtonType.PRODUCT_DISTRIBUTION]: {\r\n    value: ZNSButtonType.PRODUCT_DISTRIBUTION,\r\n    name: 'Phân phối sản phẩm',\r\n    description: 'Đến trang phân phối sản phẩm'\r\n  },\r\n  [ZNSButtonType.OTHER_WEB_APP]: {\r\n    value: ZNSButtonType.OTHER_WEB_APP,\r\n    name: 'Web/App khác',\r\n    description: 'Đến trang web/Zalo Mini App khác'\r\n  },\r\n  [ZNSButtonType.OTHER_APP]: {\r\n    value: ZNSButtonType.OTHER_APP,\r\n    name: 'App khác',\r\n    description: 'Đến ứng dụng khác'\r\n  },\r\n  [ZNSButtonType.OA_ARTICLE]: {\r\n    value: ZNSButtonType.OA_ARTICLE,\r\n    name: 'Bài viết OA',\r\n    description: 'Đến bài viết của OA'\r\n  },\r\n  [ZNSButtonType.COPY]: {\r\n    value: ZNSButtonType.COPY,\r\n    name: 'Copy',\r\n    description: 'Sao chép nội dung'\r\n  },\r\n  [ZNSButtonType.PAY_NOW]: {\r\n    value: ZNSButtonType.PAY_NOW,\r\n    name: 'Thanh toán ngay',\r\n    description: 'Thanh toán ngay'\r\n  },\r\n  [ZNSButtonType.VIEW_DETAILS]: {\r\n    value: ZNSButtonType.VIEW_DETAILS,\r\n    name: 'Xem chi tiết',\r\n    description: 'Xem chi tiết'\r\n  }\r\n} as const;\r\n\r\n/**\r\n * Parameter Type mapping với description và max length\r\n */\r\nexport const ZNS_PARAM_TYPES = {\r\n  [ZNSParamType.CUSTOMER_NAME]: {\r\n    value: ZNSParamType.CUSTOMER_NAME,\r\n    name: 'Tên khách hàng',\r\n    maxLength: 30\r\n  },\r\n  [ZNSParamType.PHONE_NUMBER]: {\r\n    value: ZNSParamType.PHONE_NUMBER,\r\n    name: 'Số điện thoại',\r\n    maxLength: 15\r\n  },\r\n  [ZNSParamType.ADDRESS]: {\r\n    value: ZNSParamType.ADDRESS,\r\n    name: 'Địa chỉ',\r\n    maxLength: 200\r\n  },\r\n  [ZNSParamType.CODE]: {\r\n    value: ZNSParamType.CODE,\r\n    name: 'Mã số',\r\n    maxLength: 30\r\n  },\r\n  [ZNSParamType.CUSTOM_LABEL]: {\r\n    value: ZNSParamType.CUSTOM_LABEL,\r\n    name: 'Nhãn tùy chỉnh',\r\n    maxLength: 30\r\n  },\r\n  [ZNSParamType.TRANSACTION_STATUS]: {\r\n    value: ZNSParamType.TRANSACTION_STATUS,\r\n    name: 'Trạng thái giao dịch',\r\n    maxLength: 30\r\n  },\r\n  [ZNSParamType.CONTACT_INFO]: {\r\n    value: ZNSParamType.CONTACT_INFO,\r\n    name: 'Thông tin liên hệ',\r\n    maxLength: 50\r\n  },\r\n  [ZNSParamType.GENDER_TITLE]: {\r\n    value: ZNSParamType.GENDER_TITLE,\r\n    name: 'Giới tính / Danh xưng',\r\n    maxLength: 5\r\n  },\r\n  [ZNSParamType.PRODUCT_BRAND]: {\r\n    value: ZNSParamType.PRODUCT_BRAND,\r\n    name: 'Tên sản phẩm / Thương hiệu',\r\n    maxLength: 200\r\n  },\r\n  [ZNSParamType.QUANTITY_AMOUNT]: {\r\n    value: ZNSParamType.QUANTITY_AMOUNT,\r\n    name: 'Số lượng / Số tiền',\r\n    maxLength: 20\r\n  },\r\n  [ZNSParamType.TIME]: {\r\n    value: ZNSParamType.TIME,\r\n    name: 'Thời gian',\r\n    maxLength: 20\r\n  },\r\n  [ZNSParamType.OTP]: {\r\n    value: ZNSParamType.OTP,\r\n    name: 'OTP',\r\n    maxLength: 10\r\n  },\r\n  [ZNSParamType.URL]: {\r\n    value: ZNSParamType.URL,\r\n    name: 'URL',\r\n    maxLength: 200\r\n  },\r\n  [ZNSParamType.CURRENCY]: {\r\n    value: ZNSParamType.CURRENCY,\r\n    name: 'Tiền tệ (VNĐ)',\r\n    maxLength: 12\r\n  },\r\n  [ZNSParamType.BANK_TRANSFER_NOTE]: {\r\n    value: ZNSParamType.BANK_TRANSFER_NOTE,\r\n    name: 'Bank transfer note',\r\n    maxLength: 90\r\n  }\r\n} as const;\r\n\r\n/**\r\n * Template Type và Tag compatibility matrix\r\n */\r\nexport const ZNS_TEMPLATE_TAG_COMPATIBILITY: Record<ZNSTemplateType, ZNSTemplateTag[]> = {\r\n  [ZNSTemplateType.CUSTOM]: [\r\n    ZNSTemplateTag.TRANSACTION,\r\n    ZNSTemplateTag.CUSTOMER_CARE,\r\n    ZNSTemplateTag.PROMOTION\r\n  ],\r\n  [ZNSTemplateType.AUTHENTICATION]: [\r\n    ZNSTemplateTag.TRANSACTION\r\n  ],\r\n  [ZNSTemplateType.PAYMENT_REQUEST]: [\r\n    ZNSTemplateTag.TRANSACTION\r\n  ],\r\n  [ZNSTemplateType.VOUCHER]: [\r\n    ZNSTemplateTag.TRANSACTION,\r\n    ZNSTemplateTag.CUSTOMER_CARE,\r\n    ZNSTemplateTag.PROMOTION\r\n  ],\r\n  [ZNSTemplateType.SERVICE_RATING]: [\r\n    ZNSTemplateTag.CUSTOMER_CARE\r\n  ]\r\n};\r\n\r\n/**\r\n * Validation functions\r\n */\r\nexport const ZNSValidation = {\r\n  /**\r\n   * Kiểm tra template name hợp lệ\r\n   */\r\n  isValidTemplateName: (name: string): boolean => {\r\n    return name.length >= 10 && name.length <= 60;\r\n  },\r\n\r\n  /**\r\n   * Kiểm tra note hợp lệ\r\n   */\r\n  isValidNote: (note: string): boolean => {\r\n    return note.length >= 1 && note.length <= 400;\r\n  },\r\n\r\n  /**\r\n   * Kiểm tra tag có tương thích với template type không\r\n   */\r\n  isTagCompatibleWithType: (templateType: ZNSTemplateType, tag: ZNSTemplateTag): boolean => {\r\n    const compatibleTags = ZNS_TEMPLATE_TAG_COMPATIBILITY[templateType];\r\n    return compatibleTags ? compatibleTags.includes(tag) : false;\r\n  },\r\n\r\n  /**\r\n   * Kiểm tra param value có hợp lệ với type không\r\n   */\r\n  isValidParamValue: (paramType: ZNSParamType, value: string): boolean => {\r\n    const typeInfo = ZNS_PARAM_TYPES[paramType];\r\n    return typeInfo ? value.length <= typeInfo.maxLength : false;\r\n  }\r\n} as const;\r\n","/**\r\n * Types for Zalo Webhook Events\r\n * Based on Zalo Official Account Webhook API\r\n */\r\n\r\n// ==================== BASE WEBHOOK TYPES ====================\r\n\r\n/**\r\n * Base webhook event structure\r\n */\r\nexport interface BaseWebhookEvent {\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User ID for Social API */\r\n  user_id_by_app: string;\r\n  /** Event name */\r\n  event_name: string;\r\n  /** Event timestamp */\r\n  timestamp: string;\r\n}\r\n\r\n/**\r\n * Webhook sender information\r\n */\r\nexport interface WebhookSender {\r\n  /** Sender ID */\r\n  id: string;\r\n}\r\n\r\n/**\r\n * Webhook recipient information\r\n */\r\nexport interface WebhookRecipient {\r\n  /** Recipient ID */\r\n  id: string;\r\n}\r\n\r\n/**\r\n * Webhook follower information (for follow events)\r\n */\r\nexport interface WebhookFollower {\r\n  /** Follower ID */\r\n  id: string;\r\n}\r\n\r\n/**\r\n * Webhook customer information (for shop events)\r\n */\r\nexport interface WebhookCustomer {\r\n  /** Customer ID */\r\n  id: string;\r\n}\r\n\r\n/**\r\n * User submitted information\r\n */\r\nexport interface UserSubmittedInfo {\r\n  /** User address */\r\n  address?: string;\r\n  /** User phone number */\r\n  phone?: string;\r\n  /** User city */\r\n  city?: string;\r\n  /** User district */\r\n  district?: string;\r\n  /** User name */\r\n  name?: string;\r\n  /** User ward */\r\n  ward?: string;\r\n}\r\n\r\n/**\r\n * Webhook user information (for click events)\r\n */\r\nexport interface WebhookUser {\r\n  /** User ID */\r\n  id?: string;\r\n}\r\n\r\n/**\r\n * Message with reaction\r\n */\r\nexport interface ReactionMessage {\r\n  /** Message ID */\r\n  msg_id: string;\r\n  /** Reaction icon */\r\n  react_icon: string;\r\n}\r\n\r\n/**\r\n * Anonymous message with conversation ID\r\n */\r\nexport interface AnonymousMessage extends WebhookBaseMessage {\r\n  /** Conversation ID for anonymous chat */\r\n  conversation_id: string;\r\n}\r\n\r\n/**\r\n * Call information for call events\r\n */\r\nexport interface CallInfo {\r\n  /** Call ID */\r\n  call_id: string;\r\n  /** Call type (AUDIO, VIDEO) */\r\n  call_type: string;\r\n  /** Call initialization time */\r\n  init_time: string;\r\n  /** Total call duration in milliseconds */\r\n  call_duration: string;\r\n  /** Waiting time before call answered */\r\n  waiting_time: string;\r\n  /** Actual talk time */\r\n  talk_time: string;\r\n  /** Call status code */\r\n  status_code: string;\r\n  /** Phone number */\r\n  phone?: string;\r\n}\r\n\r\n/**\r\n * Template message payload\r\n */\r\nexport interface TemplatePayload {\r\n  /** Template checksum */\r\n  checksum: string;\r\n  /** Template link URL */\r\n  link_url: string;\r\n  /** Zalo instant ID */\r\n  zinstant_id: string;\r\n  /** Template text content */\r\n  text: string;\r\n}\r\n\r\n/**\r\n * Template message source\r\n */\r\nexport interface TemplateSource {\r\n  /** Service name */\r\n  service: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n/**\r\n * Template message\r\n */\r\nexport interface TemplateMessage {\r\n  /** Client message ID */\r\n  client_msg_id: string;\r\n  /** Template payload */\r\n  payload: TemplatePayload;\r\n  /** Message source */\r\n  source: TemplateSource;\r\n  /** Message ID */\r\n  msg_id: string;\r\n  /** Is reply flag */\r\n  is_reply: boolean;\r\n}\r\n\r\n/**\r\n * Business card payload\r\n */\r\nexport interface BusinessCardPayload {\r\n  /** Business card thumbnail */\r\n  thumbnail: string;\r\n  /** Business card description (JSON string) */\r\n  description: string;\r\n  /** Business card URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * Business card message\r\n */\r\nexport interface BusinessCardMessage extends WebhookBaseMessage {\r\n  /** Client message ID */\r\n  client_msg_id: string;\r\n  /** Business card attachments */\r\n  attachments: Array<\r\n    MessageAttachment & { type: \"link\"; payload: BusinessCardPayload }\r\n  >;\r\n}\r\n\r\n/**\r\n * User feedback message\r\n */\r\nexport interface FeedbackMessage {\r\n  /** Feedback note */\r\n  note: string;\r\n  /** Rating score */\r\n  rate: number;\r\n  /** Submit time */\r\n  submit_time: string;\r\n  /** Feedback options */\r\n  feedbacks: string[];\r\n  /** Message ID */\r\n  msg_id: string;\r\n  /** Tracking ID */\r\n  tracking_id: string;\r\n}\r\n\r\n/**\r\n * Quota change information\r\n */\r\nexport interface QuotaChange {\r\n  /** Previous quota value */\r\n  prev_value: number;\r\n  /** New quota value */\r\n  new_value: number;\r\n}\r\n\r\n/**\r\n * Tag level change information\r\n */\r\nexport interface TagLevelChange {\r\n  /** Previous tag level */\r\n  prev_value: number;\r\n  /** New tag level */\r\n  new_value: number;\r\n}\r\n\r\n/**\r\n * ZNS delivery message\r\n */\r\nexport interface ZNSDeliveryMessage {\r\n  /** Delivery time */\r\n  delivery_time: string;\r\n  /** Message ID */\r\n  msg_id: string;\r\n  /** Tracking ID */\r\n  tracking_id: string;\r\n}\r\n\r\n/**\r\n * Group information\r\n */\r\nexport interface GroupInfo {\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list (for group events) */\r\n  users?: string[];\r\n}\r\n\r\n/**\r\n * Widget interaction data\r\n */\r\nexport interface WidgetInteractionData {\r\n  /** User ID by OA */\r\n  user_id_by_oa: string;\r\n  /** User external ID */\r\n  user_external_id: string;\r\n  /** Widget URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * Widget sync failure data\r\n */\r\nexport interface WidgetSyncFailureData {\r\n  /** Error message */\r\n  message: string;\r\n  /** User ID by OA */\r\n  user_id_by_oa: string;\r\n  /** User external ID */\r\n  user_external_id: string;\r\n}\r\n\r\n/**\r\n * Template status change\r\n */\r\nexport interface TemplateStatusChange {\r\n  /** Previous status */\r\n  prev_status: string;\r\n  /** New status */\r\n  new_status: string;\r\n}\r\n\r\n/**\r\n * Permission revoked data\r\n */\r\nexport interface PermissionRevokedData {\r\n  /** Action performed by (APP or OA) */\r\n  action_by: string;\r\n  /** Revoked timestamp */\r\n  revoked_at: string;\r\n}\r\n\r\n/**\r\n * Extension subscription info\r\n */\r\nexport interface ExtensionSubscriptionInfo {\r\n  /** Valid through date */\r\n  valid_through_date: string;\r\n  /** Duration in months */\r\n  duration_month: string;\r\n  /** Valid start date */\r\n  valid_start_date: string;\r\n}\r\n\r\n/**\r\n * Update user info data\r\n */\r\nexport interface UpdateUserInfoData {\r\n  /** Update method */\r\n  method: string;\r\n}\r\n\r\n/**\r\n * Anonymous message with attachments and conversation ID\r\n */\r\nexport interface AnonymousMessageWithAttachments\r\n  extends WebhookMessageWithAttachments {\r\n  /** Conversation ID for anonymous chat */\r\n  conversation_id: string;\r\n}\r\n\r\n// ==================== MESSAGE ATTACHMENT TYPES ====================\r\n\r\n/**\r\n * Location coordinates\r\n */\r\nexport interface LocationCoordinates {\r\n  /** Latitude */\r\n  latitude: string;\r\n  /** Longitude */\r\n  longitude: string;\r\n}\r\n\r\n/**\r\n * Location attachment payload\r\n */\r\nexport interface LocationPayload {\r\n  /** Location coordinates */\r\n  coordinates: LocationCoordinates;\r\n}\r\n\r\n/**\r\n * Image attachment payload\r\n */\r\nexport interface ImagePayload {\r\n  /** Thumbnail URL */\r\n  thumbnail: string;\r\n  /** Full image URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * Link attachment payload\r\n */\r\nexport interface LinkPayload {\r\n  /** Link thumbnail URL */\r\n  thumbnail: string;\r\n  /** Link description */\r\n  description: string;\r\n  /** Link URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * Sticker attachment payload\r\n */\r\nexport interface StickerPayload {\r\n  /** Sticker ID */\r\n  id: string;\r\n  /** Sticker URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * GIF attachment payload\r\n */\r\nexport interface GifPayload {\r\n  /** GIF thumbnail URL */\r\n  thumbnail: string;\r\n  /** GIF URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * Audio attachment payload\r\n */\r\nexport interface AudioPayload {\r\n  /** Audio file URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * Video attachment payload\r\n */\r\nexport interface VideoPayload {\r\n  /** Video thumbnail URL */\r\n  thumbnail: string;\r\n  /** Video description */\r\n  description: string;\r\n  /** Video file URL */\r\n  url: string;\r\n}\r\n\r\n/**\r\n * File attachment payload\r\n */\r\nexport interface FilePayload {\r\n  /** File download URL */\r\n  url: string;\r\n  /** File size in bytes */\r\n  size: string;\r\n  /** File name */\r\n  name: string;\r\n  /** File checksum */\r\n  checksum: string;\r\n  /** File type/MIME type */\r\n  type: string;\r\n}\r\n\r\n/**\r\n * Enhanced link payload for OA list messages\r\n */\r\nexport interface EnhancedLinkPayload extends LinkPayload {\r\n  /** Link title */\r\n  title: string;\r\n}\r\n\r\n/**\r\n * Union type for all attachment payloads\r\n */\r\nexport type AttachmentPayload =\r\n  | LocationPayload\r\n  | ImagePayload\r\n  | LinkPayload\r\n  | StickerPayload\r\n  | GifPayload\r\n  | AudioPayload\r\n  | VideoPayload\r\n  | FilePayload\r\n  | EnhancedLinkPayload;\r\n\r\n/**\r\n * Message attachment\r\n */\r\nexport interface MessageAttachment {\r\n  /** Attachment payload */\r\n  payload: AttachmentPayload;\r\n  /** Attachment type */\r\n  type:\r\n    | \"location\"\r\n    | \"image\"\r\n    | \"link\"\r\n    | \"sticker\"\r\n    | \"gif\"\r\n    | \"audio\"\r\n    | \"video\"\r\n    | \"file\";\r\n}\r\n\r\n// ==================== MESSAGE TYPES ====================\r\n\r\n/**\r\n * Base webhook message structure\r\n */\r\nexport interface WebhookBaseMessage {\r\n  /** Message ID */\r\n  msg_id: string;\r\n  /** Quoted message ID when the message is a reply */\r\n  quote_msg_id?: string;\r\n  /** Message text content */\r\n  text?: string;\r\n}\r\n\r\n/**\r\n * Webhook message with attachments\r\n */\r\nexport interface WebhookMessageWithAttachments extends WebhookBaseMessage {\r\n  /** Message attachments */\r\n  attachments: MessageAttachment[];\r\n}\r\n\r\n/**\r\n * Message for seen event (contains multiple message IDs)\r\n */\r\nexport interface SeenMessage {\r\n  /** Array of message IDs that were seen */\r\n  msg_ids: string[];\r\n}\r\n\r\n/**\r\n * User follows Official Account event\r\n */\r\nexport interface UserFollowEvent extends BaseWebhookEvent {\r\n  event_name: \"follow\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Event source */\r\n  source: string;\r\n  /** Follower information */\r\n  follower: WebhookFollower;\r\n}\r\n\r\n/**\r\n * User unfollows Official Account event\r\n */\r\nexport interface UserUnfollowEvent extends BaseWebhookEvent {\r\n  event_name: \"unfollow\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Event source */\r\n  source: string;\r\n  /** Follower information */\r\n  follower: WebhookFollower;\r\n}\r\n\r\n/**\r\n * Shop has order event\r\n */\r\nexport interface ShopHasOrderEvent extends BaseWebhookEvent {\r\n  event_name: \"shop_has_order\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Customer information */\r\n  customer: WebhookCustomer;\r\n}\r\n\r\n/**\r\n * Add user to tag event\r\n */\r\nexport interface AddUserToTagEvent extends BaseWebhookEvent {\r\n  event_name: \"add_user_to_tag\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n}\r\n\r\n/**\r\n * OA sends text message event\r\n */\r\nexport interface OASendTextEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_text\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookBaseMessage;\r\n}\r\n\r\n/**\r\n * OA sends image message event\r\n */\r\nexport interface OASendImageEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_image\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"image\"; payload: ImagePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA sends GIF message event\r\n */\r\nexport interface OASendGifEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_gif\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"gif\"; payload: GifPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA sends list message event (interactive message with multiple links)\r\n */\r\nexport interface OASendListEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_list\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"link\"; payload: EnhancedLinkPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA sends file message event\r\n */\r\nexport interface OASendFileEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_file\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"file\"; payload: FilePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA sends sticker message event\r\n */\r\nexport interface OASendStickerEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_sticker\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"sticker\"; payload: StickerPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User clicks \"Chat Now\" button event\r\n */\r\nexport interface UserClickChatNowEvent extends BaseWebhookEvent {\r\n  event_name: \"user_click_chatnow\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** User ID (for OA API) */\r\n  user_id: string;\r\n}\r\n\r\n/**\r\n * User reacts to message event\r\n */\r\nexport interface UserReactedMessageEvent extends BaseWebhookEvent {\r\n  event_name: \"user_reacted_message\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: ReactionMessage;\r\n}\r\n\r\n/**\r\n * OA reacts to message event\r\n */\r\nexport interface OAReactedMessageEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_reacted_message\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: ReactionMessage;\r\n}\r\n\r\n/**\r\n * OA sends consent request event\r\n */\r\nexport interface OASendConsentEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_consent\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Request type */\r\n  request_type: string;\r\n  /** Create time */\r\n  create_time: string;\r\n  /** Expired time */\r\n  expired_time: string;\r\n  /** Phone number */\r\n  phone: string;\r\n}\r\n\r\n/**\r\n * User replies to consent request event\r\n */\r\nexport interface UserReplyConsentEvent extends BaseWebhookEvent {\r\n  event_name: \"user_reply_consent\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Expired time */\r\n  expired_time: string;\r\n  /** Confirmed time */\r\n  confirmed_time: string;\r\n  /** Phone number */\r\n  phone: string;\r\n}\r\n\r\n/**\r\n * Anonymous user sends text message event\r\n */\r\nexport interface AnonymousSendTextEvent extends BaseWebhookEvent {\r\n  event_name: \"anonymous_send_text\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessage;\r\n}\r\n\r\n/**\r\n * Anonymous user sends image message event\r\n */\r\nexport interface AnonymousSendImageEvent extends BaseWebhookEvent {\r\n  event_name: \"anonymous_send_image\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"image\"; payload: ImagePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * Anonymous user sends file message event\r\n */\r\nexport interface AnonymousSendFileEvent extends BaseWebhookEvent {\r\n  event_name: \"anonymous_send_file\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"file\"; payload: FilePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * Anonymous user sends sticker message event\r\n */\r\nexport interface AnonymousSendStickerEvent extends BaseWebhookEvent {\r\n  event_name: \"anonymous_send_sticker\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"sticker\"; payload: StickerPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA sends anonymous text message event\r\n */\r\nexport interface OASendAnonymousTextEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_anonymous_text\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessage;\r\n}\r\n\r\n/**\r\n * OA sends anonymous image message event\r\n */\r\nexport interface OASendAnonymousImageEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_anonymous_image\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"image\"; payload: ImagePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA sends anonymous file message event\r\n */\r\nexport interface OASendAnonymousFileEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_anonymous_file\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"file\"; payload: FilePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA sends anonymous sticker message event\r\n */\r\nexport interface OASendAnonymousStickerEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_anonymous_sticker\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: AnonymousMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"sticker\"; payload: StickerPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA call user event\r\n */\r\nexport interface OACallUserEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_call_user\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Call information */\r\n  call_id: string;\r\n  call_type: string;\r\n  init_time: string;\r\n  call_duration: string;\r\n  waiting_time: string;\r\n  talk_time: string;\r\n  status_code: string;\r\n  phone: string;\r\n}\r\n\r\n/**\r\n * User call OA event\r\n */\r\nexport interface UserCallOAEvent extends BaseWebhookEvent {\r\n  event_name: \"user_call_oa\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** User ID (for OA API) */\r\n  user_id: string;\r\n  /** Call information */\r\n  call_id: string;\r\n  call_type: string;\r\n  init_time: string;\r\n  call_duration: string;\r\n  waiting_time: string;\r\n  talk_time: string;\r\n  status_code: string;\r\n}\r\n\r\n/**\r\n * OA sends template message event\r\n */\r\nexport interface OASendTemplateEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_template\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: TemplateMessage;\r\n}\r\n\r\n/**\r\n * User sends business card event\r\n */\r\nexport interface UserSendBusinessCardEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_business_card\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: BusinessCardMessage;\r\n}\r\n\r\n/**\r\n * User feedback event\r\n */\r\nexport interface UserFeedbackEvent extends BaseWebhookEvent {\r\n  event_name: \"user_feedback\";\r\n  message: FeedbackMessage;\r\n}\r\n\r\n/**\r\n * Change OA daily quota event\r\n */\r\nexport interface ChangeOADailyQuotaEvent extends BaseWebhookEvent {\r\n  event_name: \"change_oa_daily_quota\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Quota change information */\r\n  quota: QuotaChange;\r\n}\r\n\r\n/**\r\n * Change OA template tags event\r\n */\r\nexport interface ChangeOATemplateTagsEvent extends BaseWebhookEvent {\r\n  event_name: \"change_oa_template_tags\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Tag level change information */\r\n  tag_level: TagLevelChange;\r\n}\r\n\r\n/**\r\n * Change template quality event\r\n */\r\nexport interface ChangeTemplateQualityEvent extends BaseWebhookEvent {\r\n  event_name: \"change_template_quality\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Template ID */\r\n  template_id: string;\r\n  /** Template quality */\r\n  quality: string;\r\n}\r\n\r\n/**\r\n * Change template quota event\r\n */\r\nexport interface ChangeTemplateQuotaEvent extends BaseWebhookEvent {\r\n  event_name: \"change_template_quota\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Template ID */\r\n  template_id: string;\r\n  /** Quota change information */\r\n  quota: QuotaChange;\r\n}\r\n\r\n/**\r\n * Journey timeout event\r\n */\r\nexport interface JourneyTimeoutEvent extends BaseWebhookEvent {\r\n  event_name: \"event_journey_time_out\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Journey ID */\r\n  journey_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n/**\r\n * Journey acknowledged event\r\n */\r\nexport interface JourneyAcknowledgedEvent extends BaseWebhookEvent {\r\n  event_name: \"event_journey_acknowledged\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Journey ID */\r\n  journey_id: string;\r\n  /** Message ID */\r\n  msg_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n/**\r\n * ZNS user received message event\r\n */\r\nexport interface ZNSUserReceivedMessageEvent extends BaseWebhookEvent {\r\n  event_name: \"user_received_message\";\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** ZNS delivery message */\r\n  message: ZNSDeliveryMessage;\r\n}\r\n\r\n/**\r\n * Create group event\r\n */\r\nexport interface CreateGroupEvent extends BaseWebhookEvent {\r\n  event_name: \"create_group\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n/**\r\n * User join group event\r\n */\r\nexport interface UserJoinGroupEvent extends BaseWebhookEvent {\r\n  event_name: \"user_join_group\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list */\r\n  users: string[];\r\n}\r\n\r\n/**\r\n * User request join group event\r\n */\r\nexport interface UserRequestJoinGroupEvent extends BaseWebhookEvent {\r\n  event_name: \"user_request_join_group\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list */\r\n  users: string[];\r\n}\r\n\r\n/**\r\n * React request join group event\r\n */\r\nexport interface ReactRequestJoinGroupEvent extends BaseWebhookEvent {\r\n  event_name: \"react_request_join_group\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list */\r\n  users: string[];\r\n}\r\n\r\n/**\r\n * Reject request join group event\r\n */\r\nexport interface RejectRequestJoinGroupEvent extends BaseWebhookEvent {\r\n  event_name: \"reject_request_join_group\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list */\r\n  users: string[];\r\n}\r\n\r\n/**\r\n * Add group admin event\r\n */\r\nexport interface AddGroupAdminEvent extends BaseWebhookEvent {\r\n  event_name: \"add_group_admin\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list */\r\n  users: string[];\r\n}\r\n\r\n/**\r\n * Remove group admin event\r\n */\r\nexport interface RemoveGroupAdminEvent extends BaseWebhookEvent {\r\n  event_name: \"remove_group_admin\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list */\r\n  users: string[];\r\n}\r\n\r\n/**\r\n * Update group info event\r\n */\r\nexport interface UpdateGroupInfoEvent extends BaseWebhookEvent {\r\n  event_name: \"update_group_info\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n/**\r\n * User out group event\r\n */\r\nexport interface UserOutGroupEvent extends BaseWebhookEvent {\r\n  event_name: \"user_out_group\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Group ID */\r\n  group_id: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n  /** User list */\r\n  users: string[];\r\n}\r\n\r\n/**\r\n * OA send group text event\r\n */\r\nexport interface OASendGroupTextEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_text\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookBaseMessage;\r\n}\r\n\r\n/**\r\n * OA send group image event\r\n */\r\nexport interface OASendGroupImageEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_image\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"image\"; payload: ImagePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send group link event\r\n */\r\nexport interface OASendGroupLinkEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_link\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"link\"; payload: LinkPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send group audio event\r\n */\r\nexport interface OASendGroupAudioEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_audio\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"audio\"; payload: AudioPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send group location event\r\n */\r\nexport interface OASendGroupLocationEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_location\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"location\"; payload: LocationPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send group video event\r\n */\r\nexport interface OASendGroupVideoEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_video\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"video\"; payload: VideoPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send group business card event\r\n */\r\nexport interface OASendGroupBusinessCardEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_business_card\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: BusinessCardMessage;\r\n}\r\n\r\n/**\r\n * OA send group sticker event\r\n */\r\nexport interface OASendGroupStickerEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_sticker\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"sticker\"; payload: StickerPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send group GIF event\r\n */\r\nexport interface OASendGroupGifEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_gif\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"gif\"; payload: GifPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send group file event\r\n */\r\nexport interface OASendGroupFileEvent extends BaseWebhookEvent {\r\n  event_name: \"oa_send_group_file\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"file\"; payload: FilePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group text event\r\n */\r\nexport interface UserSendGroupTextEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_text\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookBaseMessage;\r\n}\r\n\r\n/**\r\n * User send group image event\r\n */\r\nexport interface UserSendGroupImageEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_image\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"image\"; payload: ImagePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group link event\r\n */\r\nexport interface UserSendGroupLinkEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_link\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"link\"; payload: LinkPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group audio event\r\n */\r\nexport interface UserSendGroupAudioEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_audio\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"audio\"; payload: AudioPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group location event\r\n */\r\nexport interface UserSendGroupLocationEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_location\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"location\"; payload: LocationPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group video event\r\n */\r\nexport interface UserSendGroupVideoEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_video\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"video\"; payload: VideoPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group business card event\r\n */\r\nexport interface UserSendGroupBusinessCardEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_business_card\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: BusinessCardMessage;\r\n}\r\n\r\n/**\r\n * User send group sticker event\r\n */\r\nexport interface UserSendGroupStickerEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_sticker\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"sticker\"; payload: StickerPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group GIF event\r\n */\r\nexport interface UserSendGroupGifEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_gif\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"gif\"; payload: GifPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User send group file event\r\n */\r\nexport interface UserSendGroupFileEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_group_file\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"file\"; payload: FilePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * Widget interaction accepted event\r\n */\r\nexport interface WidgetInteractionAcceptedEvent extends BaseWebhookEvent {\r\n  event_name: \"widget_interaction_accepted\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Widget interaction data */\r\n  data: WidgetInteractionData;\r\n}\r\n\r\n/**\r\n * Widget failed to sync user external ID event\r\n */\r\nexport interface WidgetFailedToSyncUserExternalIdEvent\r\n  extends BaseWebhookEvent {\r\n  event_name: \"widget_failed_to_sync_user_external_id\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Widget sync failure data */\r\n  data: WidgetSyncFailureData;\r\n}\r\n\r\n/**\r\n * Change template status event\r\n */\r\nexport interface ChangeTemplateStatusEvent extends BaseWebhookEvent {\r\n  event_name: \"change_template_status\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Template ID */\r\n  template_id: string;\r\n  /** Status change information */\r\n  status: TemplateStatusChange;\r\n  /** Reason for status change */\r\n  reason: string;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n/**\r\n * Permission revoked event\r\n */\r\nexport interface PermissionRevokedEvent extends BaseWebhookEvent {\r\n  event_name: \"permission_revoked\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Permission revoked data */\r\n  data: PermissionRevokedData;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n/**\r\n * Extension purchased event\r\n */\r\nexport interface ExtensionPurchasedEvent extends BaseWebhookEvent {\r\n  event_name: \"extension_purchased\";\r\n  /** Extension ID */\r\n  extension_id: string;\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** Extension subscription info */\r\n  extension_sub_info: ExtensionSubscriptionInfo;\r\n}\r\n\r\n/**\r\n * Update user info event\r\n */\r\nexport interface UpdateUserInfoEvent extends BaseWebhookEvent {\r\n  event_name: \"update_user_info\";\r\n  /** Official Account ID */\r\n  oa_id: string;\r\n  /** User ID (for OA API) */\r\n  user_id: string;\r\n  /** Update user info data */\r\n  data: UpdateUserInfoData;\r\n  /** Application ID */\r\n  app_id: string;\r\n}\r\n\r\n// ==================== SPECIFIC WEBHOOK EVENT TYPES ====================\r\n\r\n/**\r\n * User sends location event\r\n */\r\nexport interface UserSendLocationEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_location\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"location\"; payload: LocationPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User sends image event\r\n */\r\nexport interface UserSendImageEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_image\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"image\"; payload: ImagePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User sends link event\r\n */\r\nexport interface UserSendLinkEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_link\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"link\"; payload: LinkPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User sends text event\r\n */\r\nexport interface UserSendTextEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_text\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookBaseMessage;\r\n}\r\n\r\n/**\r\n * User sends sticker event\r\n */\r\nexport interface UserSendStickerEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_sticker\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"sticker\"; payload: StickerPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User sends GIF event\r\n */\r\nexport interface UserSendGifEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_gif\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"gif\"; payload: GifPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User sends audio event\r\n */\r\nexport interface UserSendAudioEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_audio\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"audio\"; payload: AudioPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User received message event\r\n */\r\nexport interface UserReceivedMessageEvent extends BaseWebhookEvent {\r\n  event_name: \"user_received_message\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookBaseMessage;\r\n}\r\n\r\n/**\r\n * User seen message event\r\n */\r\nexport interface UserSeenMessageEvent extends BaseWebhookEvent {\r\n  event_name: \"user_seen_message\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: SeenMessage;\r\n}\r\n\r\n/**\r\n * User sends video event\r\n */\r\nexport interface UserSendVideoEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_video\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"video\"; payload: VideoPayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User sends file event\r\n */\r\nexport interface UserSendFileEvent extends BaseWebhookEvent {\r\n  event_name: \"user_send_file\";\r\n  sender: WebhookSender;\r\n  recipient: WebhookRecipient;\r\n  message: WebhookMessageWithAttachments & {\r\n    attachments: Array<\r\n      MessageAttachment & { type: \"file\"; payload: FilePayload }\r\n    >;\r\n  };\r\n}\r\n\r\n/**\r\n * User submit info event\r\n */\r\nexport interface UserSubmitInfoEvent extends BaseWebhookEvent {\r\n  event_name: \"user_submit_info\";\r\n  sender: {\r\n    id: string;\r\n  };\r\n  recipient: {\r\n    id: string;\r\n  };\r\n  info: {\r\n    name?: string;\r\n    phone?: string;\r\n    email?: string;\r\n    address?: string;\r\n    city?: string;\r\n    district?: string;\r\n    ward?: string;\r\n    date_of_birth?: string;\r\n  };\r\n}\r\n\r\n/**\r\n * User click button event\r\n */\r\nexport interface UserClickButtonEvent extends BaseWebhookEvent {\r\n  event_name: \"user_click_chatnow\" | \"user_click_button\";\r\n  sender: {\r\n    id: string;\r\n  };\r\n  recipient: {\r\n    id: string;\r\n  };\r\n  button?: {\r\n    title: string;\r\n    payload: string;\r\n  };\r\n}\r\n\r\n/**\r\n * OA send message event\r\n */\r\nexport interface OASendMessageEvent extends BaseWebhookEvent {\r\n  event_name:\r\n    | \"oa_send_text\"\r\n    | \"oa_send_image\"\r\n    | \"oa_send_file\"\r\n    | \"oa_send_sticker\"\r\n    | \"oa_send_gif\"\r\n    | \"oa_send_audio\"\r\n    | \"oa_send_video\"\r\n    | \"oa_send_list\";\r\n  sender: {\r\n    id: string;\r\n  };\r\n  recipient: {\r\n    id: string;\r\n  };\r\n  message: {\r\n    text?: string;\r\n    msg_id: string;\r\n    /** Quoted message ID when the message is a reply */\r\n    quote_msg_id?: string;\r\n    attachments?: Array<{\r\n      type: string;\r\n      payload: {\r\n        url?: string;\r\n        title?: string;\r\n        description?: string;\r\n      };\r\n    }>;\r\n  };\r\n}\r\n\r\n/**\r\n * Anonymous user send message event\r\n */\r\nexport interface AnonymousUserSendMessageEvent extends BaseWebhookEvent {\r\n  event_name:\r\n    | \"anonymous_send_text\"\r\n    | \"anonymous_send_image\"\r\n    | \"anonymous_send_sticker\"\r\n    | \"anonymous_send_file\";\r\n  sender: {\r\n    id: string;\r\n  };\r\n  recipient: {\r\n    id: string;\r\n  };\r\n  message: {\r\n    text?: string;\r\n    msg_id: string;\r\n    /** Quoted message ID when the message is a reply */\r\n    quote_msg_id?: string;\r\n    attachments?: Array<{\r\n      type: string;\r\n      payload: {\r\n        url?: string;\r\n      };\r\n    }>;\r\n  };\r\n}\r\n\r\n/**\r\n * ZNS message status event\r\n */\r\nexport interface ZNSMessageStatusEvent extends BaseWebhookEvent {\r\n  event_name: \"zns_message_status\";\r\n  message_id: string;\r\n  phone: string;\r\n  status: \"sent\" | \"delivered\" | \"read\" | \"failed\";\r\n  error_code?: number;\r\n  error_message?: string;\r\n  timestamp: string;\r\n}\r\n\r\n/**\r\n * Template status event\r\n */\r\nexport interface TemplateStatusEvent extends BaseWebhookEvent {\r\n  event_name: \"template_status_update\";\r\n  template_id: string;\r\n  template_name: string;\r\n  status: \"ENABLE\" | \"PENDING_REVIEW\" | \"REJECT\" | \"DISABLE\" | \"DELETE\";\r\n  reason?: string;\r\n  quality?: \"HIGH\" | \"MEDIUM\" | \"LOW\" | \"UNDEFINED\";\r\n}\r\n\r\n/**\r\n * Group message events\r\n */\r\nexport interface GroupUserJoinEvent extends BaseWebhookEvent {\r\n  event_name: \"group_user_join\";\r\n  group_id: string;\r\n  user: {\r\n    id: string;\r\n    name: string;\r\n  };\r\n}\r\n\r\nexport interface GroupUserLeaveEvent extends BaseWebhookEvent {\r\n  event_name: \"group_user_leave\";\r\n  group_id: string;\r\n  user: {\r\n    id: string;\r\n    name: string;\r\n  };\r\n}\r\n\r\nexport interface GroupSendMessageEvent extends BaseWebhookEvent {\r\n  event_name:\r\n    | \"group_send_text\"\r\n    | \"group_send_image\"\r\n    | \"group_send_sticker\"\r\n    | \"group_send_file\";\r\n  group_id: string;\r\n  sender: {\r\n    id: string;\r\n    name: string;\r\n  };\r\n  message: {\r\n    text?: string;\r\n    msg_id: string;\r\n    /** Quoted message ID when the message is a reply */\r\n    quote_msg_id?: string;\r\n    attachments?: Array<{\r\n      type: string;\r\n      payload: {\r\n        url?: string;\r\n      };\r\n    }>;\r\n  };\r\n}\r\n\r\n// ==================== UNION TYPES ====================\r\n\r\n/**\r\n * All user message-related webhook events\r\n */\r\nexport type UserMessageWebhookEvent =\r\n  | UserSendLocationEvent\r\n  | UserSendImageEvent\r\n  | UserSendLinkEvent\r\n  | UserSendTextEvent\r\n  | UserSendStickerEvent\r\n  | UserSendGifEvent\r\n  | UserSendAudioEvent\r\n  | UserSendVideoEvent\r\n  | UserSendFileEvent\r\n  | UserReceivedMessageEvent\r\n  | UserSeenMessageEvent;\r\n\r\n/**\r\n * All OA message-related webhook events\r\n */\r\nexport type OAMessageWebhookEvent =\r\n  | OASendTextEvent\r\n  | OASendImageEvent\r\n  | OASendGifEvent\r\n  | OASendListEvent\r\n  | OASendFileEvent\r\n  | OASendStickerEvent\r\n  | OASendAnonymousTextEvent\r\n  | OASendAnonymousImageEvent\r\n  | OASendAnonymousFileEvent\r\n  | OASendAnonymousStickerEvent\r\n  | OASendTemplateEvent;\r\n\r\n/**\r\n * All message-related webhook events\r\n */\r\nexport type MessageWebhookEvent =\r\n  | UserMessageWebhookEvent\r\n  | OAMessageWebhookEvent;\r\n\r\n/**\r\n * All user action webhook events\r\n */\r\nexport type UserActionWebhookEvent =\r\n  | UserFollowEvent\r\n  | UserUnfollowEvent\r\n  | UserSubmitInfoEvent\r\n  | UserClickChatNowEvent\r\n  | UserReactedMessageEvent\r\n  | UserReplyConsentEvent\r\n  | UserSendBusinessCardEvent\r\n  | UserFeedbackEvent;\r\n\r\n/**\r\n * All shop/business webhook events\r\n */\r\nexport type ShopWebhookEvent = ShopHasOrderEvent;\r\n\r\n/**\r\n * All call-related webhook events\r\n */\r\nexport type CallWebhookEvent = OACallUserEvent | UserCallOAEvent;\r\n\r\n/**\r\n * All ZNS-related webhook events\r\n */\r\nexport type ZNSWebhookEvent =\r\n  | ChangeOADailyQuotaEvent\r\n  | ChangeOATemplateTagsEvent\r\n  | ChangeTemplateQualityEvent\r\n  | ChangeTemplateQuotaEvent\r\n  | JourneyTimeoutEvent\r\n  | JourneyAcknowledgedEvent\r\n  | ZNSUserReceivedMessageEvent\r\n  | ChangeTemplateStatusEvent;\r\n\r\n/**\r\n * All widget-related webhook events\r\n */\r\nexport type WidgetWebhookEvent =\r\n  | WidgetInteractionAcceptedEvent\r\n  | WidgetFailedToSyncUserExternalIdEvent;\r\n\r\n/**\r\n * All group-related webhook events\r\n */\r\nexport type GroupWebhookEvent =\r\n  | CreateGroupEvent\r\n  | UserJoinGroupEvent\r\n  | UserRequestJoinGroupEvent\r\n  | ReactRequestJoinGroupEvent\r\n  | RejectRequestJoinGroupEvent\r\n  | AddGroupAdminEvent\r\n  | RemoveGroupAdminEvent\r\n  | UpdateGroupInfoEvent\r\n  | UserOutGroupEvent\r\n  | OASendGroupTextEvent\r\n  | OASendGroupImageEvent\r\n  | OASendGroupLinkEvent\r\n  | OASendGroupAudioEvent\r\n  | OASendGroupLocationEvent\r\n  | OASendGroupVideoEvent\r\n  | OASendGroupBusinessCardEvent\r\n  | OASendGroupStickerEvent\r\n  | OASendGroupGifEvent\r\n  | OASendGroupFileEvent\r\n  | UserSendGroupTextEvent\r\n  | UserSendGroupImageEvent\r\n  | UserSendGroupLinkEvent\r\n  | UserSendGroupAudioEvent\r\n  | UserSendGroupLocationEvent\r\n  | UserSendGroupVideoEvent\r\n  | UserSendGroupBusinessCardEvent\r\n  | UserSendGroupStickerEvent\r\n  | UserSendGroupGifEvent\r\n  | UserSendGroupFileEvent;\r\n\r\n/**\r\n * All system/admin webhook events\r\n */\r\nexport type SystemWebhookEvent =\r\n  | AddUserToTagEvent\r\n  | OAReactedMessageEvent\r\n  | OASendConsentEvent\r\n  | PermissionRevokedEvent\r\n  | ExtensionPurchasedEvent\r\n  | UpdateUserInfoEvent;\r\n\r\n/**\r\n * All anonymous user webhook events\r\n */\r\nexport type AnonymousWebhookEvent =\r\n  | AnonymousSendTextEvent\r\n  | AnonymousSendImageEvent\r\n  | AnonymousSendFileEvent\r\n  | AnonymousSendStickerEvent;\r\n\r\n/**\r\n * Legacy webhook events (keeping for backward compatibility)\r\n */\r\nexport type LegacyWebhookEvent =\r\n  | UserSubmitInfoEvent\r\n  | UserClickButtonEvent\r\n  | OASendMessageEvent\r\n  | AnonymousUserSendMessageEvent\r\n  | ZNSMessageStatusEvent\r\n  | TemplateStatusEvent\r\n  | GroupUserJoinEvent\r\n  | GroupUserLeaveEvent\r\n  | GroupSendMessageEvent;\r\n\r\n/**\r\n * All webhook events union type\r\n */\r\nexport type WebhookEvent =\r\n  | MessageWebhookEvent\r\n  | UserActionWebhookEvent\r\n  | ShopWebhookEvent\r\n  | SystemWebhookEvent\r\n  | AnonymousWebhookEvent\r\n  | CallWebhookEvent\r\n  | ZNSWebhookEvent\r\n  | GroupWebhookEvent\r\n  | WidgetWebhookEvent\r\n  | LegacyWebhookEvent;\r\n\r\n/**\r\n * Webhook payload wrapper\r\n */\r\nexport interface WebhookPayload {\r\n  /**\r\n   * Event data\r\n   */\r\n  data: WebhookEvent;\r\n\r\n  /**\r\n   * Event signature for verification\r\n   */\r\n  signature?: string;\r\n}\r\n\r\n/**\r\n * Webhook verification result\r\n */\r\nexport interface WebhookVerification {\r\n  /**\r\n   * Whether the webhook is valid\r\n   */\r\n  valid: boolean;\r\n\r\n  /**\r\n   * Error message if invalid\r\n   */\r\n  error?: string;\r\n}\r\n\r\n/**\r\n * Webhook handler function type\r\n */\r\nexport type WebhookHandler<T extends WebhookEvent = WebhookEvent> = (\r\n  event: T\r\n) => Promise<void> | void;\r\n\r\n/**\r\n * Webhook event handlers map\r\n */\r\nexport interface WebhookHandlers {\r\n  // User message event handlers\r\n  user_send_text?: WebhookHandler<UserSendTextEvent>;\r\n  user_send_image?: WebhookHandler<UserSendImageEvent>;\r\n  user_send_location?: WebhookHandler<UserSendLocationEvent>;\r\n  user_send_link?: WebhookHandler<UserSendLinkEvent>;\r\n  user_send_sticker?: WebhookHandler<UserSendStickerEvent>;\r\n  user_send_gif?: WebhookHandler<UserSendGifEvent>;\r\n  user_send_audio?: WebhookHandler<UserSendAudioEvent>;\r\n  user_send_video?: WebhookHandler<UserSendVideoEvent>;\r\n  user_send_file?: WebhookHandler<UserSendFileEvent>;\r\n  user_received_message?: WebhookHandler<UserReceivedMessageEvent>;\r\n  user_seen_message?: WebhookHandler<UserSeenMessageEvent>;\r\n\r\n  // User action event handlers\r\n  follow?: WebhookHandler<UserFollowEvent>;\r\n  unfollow?: WebhookHandler<UserUnfollowEvent>;\r\n  user_submit_info?: WebhookHandler<UserSubmitInfoEvent>;\r\n\r\n  // OA message event handlers\r\n  oa_send_text?: WebhookHandler<OASendTextEvent>;\r\n  oa_send_image?: WebhookHandler<OASendImageEvent>;\r\n  oa_send_gif?: WebhookHandler<OASendGifEvent>;\r\n  oa_send_list?: WebhookHandler<OASendListEvent>;\r\n  oa_send_file?: WebhookHandler<OASendFileEvent>;\r\n  oa_send_sticker?: WebhookHandler<OASendStickerEvent>;\r\n\r\n  // User interaction event handlers\r\n  user_click_chatnow?: WebhookHandler<UserClickChatNowEvent>;\r\n  user_reacted_message?: WebhookHandler<UserReactedMessageEvent>;\r\n  user_reply_consent?: WebhookHandler<UserReplyConsentEvent>;\r\n\r\n  // OA interaction event handlers\r\n  oa_reacted_message?: WebhookHandler<OAReactedMessageEvent>;\r\n  oa_send_consent?: WebhookHandler<OASendConsentEvent>;\r\n\r\n  // Anonymous user event handlers\r\n  anonymous_send_text?: WebhookHandler<AnonymousSendTextEvent>;\r\n  anonymous_send_image?: WebhookHandler<AnonymousSendImageEvent>;\r\n  anonymous_send_file?: WebhookHandler<AnonymousSendFileEvent>;\r\n  anonymous_send_sticker?: WebhookHandler<AnonymousSendStickerEvent>;\r\n\r\n  // OA anonymous event handlers\r\n  oa_send_anonymous_text?: WebhookHandler<OASendAnonymousTextEvent>;\r\n  oa_send_anonymous_image?: WebhookHandler<OASendAnonymousImageEvent>;\r\n  oa_send_anonymous_file?: WebhookHandler<OASendAnonymousFileEvent>;\r\n  oa_send_anonymous_sticker?: WebhookHandler<OASendAnonymousStickerEvent>;\r\n\r\n  // Call event handlers\r\n  oa_call_user?: WebhookHandler<OACallUserEvent>;\r\n  user_call_oa?: WebhookHandler<UserCallOAEvent>;\r\n\r\n  // Template and business card event handlers\r\n  oa_send_template?: WebhookHandler<OASendTemplateEvent>;\r\n  user_send_business_card?: WebhookHandler<UserSendBusinessCardEvent>;\r\n\r\n  // Feedback event handlers\r\n  user_feedback?: WebhookHandler<UserFeedbackEvent>;\r\n\r\n  // ZNS event handlers\r\n  change_oa_daily_quota?: WebhookHandler<ChangeOADailyQuotaEvent>;\r\n  change_oa_template_tags?: WebhookHandler<ChangeOATemplateTagsEvent>;\r\n  change_template_quality?: WebhookHandler<ChangeTemplateQualityEvent>;\r\n  change_template_quota?: WebhookHandler<ChangeTemplateQuotaEvent>;\r\n  change_template_status?: WebhookHandler<ChangeTemplateStatusEvent>;\r\n  event_journey_time_out?: WebhookHandler<JourneyTimeoutEvent>;\r\n  event_journey_acknowledged?: WebhookHandler<JourneyAcknowledgedEvent>;\r\n  zns_user_received_message?: WebhookHandler<ZNSUserReceivedMessageEvent>;\r\n\r\n  // Group event handlers\r\n  create_group?: WebhookHandler<CreateGroupEvent>;\r\n  user_join_group?: WebhookHandler<UserJoinGroupEvent>;\r\n  user_request_join_group?: WebhookHandler<UserRequestJoinGroupEvent>;\r\n  react_request_join_group?: WebhookHandler<ReactRequestJoinGroupEvent>;\r\n  reject_request_join_group?: WebhookHandler<RejectRequestJoinGroupEvent>;\r\n  add_group_admin?: WebhookHandler<AddGroupAdminEvent>;\r\n  remove_group_admin?: WebhookHandler<RemoveGroupAdminEvent>;\r\n  update_group_info?: WebhookHandler<UpdateGroupInfoEvent>;\r\n  user_out_group?: WebhookHandler<UserOutGroupEvent>;\r\n  oa_send_group_text?: WebhookHandler<OASendGroupTextEvent>;\r\n  oa_send_group_image?: WebhookHandler<OASendGroupImageEvent>;\r\n  oa_send_group_link?: WebhookHandler<OASendGroupLinkEvent>;\r\n  oa_send_group_audio?: WebhookHandler<OASendGroupAudioEvent>;\r\n  oa_send_group_location?: WebhookHandler<OASendGroupLocationEvent>;\r\n  oa_send_group_video?: WebhookHandler<OASendGroupVideoEvent>;\r\n  oa_send_group_business_card?: WebhookHandler<OASendGroupBusinessCardEvent>;\r\n  oa_send_group_sticker?: WebhookHandler<OASendGroupStickerEvent>;\r\n  oa_send_group_gif?: WebhookHandler<OASendGroupGifEvent>;\r\n  oa_send_group_file?: WebhookHandler<OASendGroupFileEvent>;\r\n  user_send_group_text?: WebhookHandler<UserSendGroupTextEvent>;\r\n  user_send_group_image?: WebhookHandler<UserSendGroupImageEvent>;\r\n  user_send_group_link?: WebhookHandler<UserSendGroupLinkEvent>;\r\n  user_send_group_audio?: WebhookHandler<UserSendGroupAudioEvent>;\r\n  user_send_group_location?: WebhookHandler<UserSendGroupLocationEvent>;\r\n  user_send_group_video?: WebhookHandler<UserSendGroupVideoEvent>;\r\n  user_send_group_business_card?: WebhookHandler<UserSendGroupBusinessCardEvent>;\r\n  user_send_group_sticker?: WebhookHandler<UserSendGroupStickerEvent>;\r\n  user_send_group_gif?: WebhookHandler<UserSendGroupGifEvent>;\r\n  user_send_group_file?: WebhookHandler<UserSendGroupFileEvent>;\r\n\r\n  // Widget event handlers\r\n  widget_interaction_accepted?: WebhookHandler<WidgetInteractionAcceptedEvent>;\r\n  widget_failed_to_sync_user_external_id?: WebhookHandler<WidgetFailedToSyncUserExternalIdEvent>;\r\n\r\n  // System event handlers\r\n  permission_revoked?: WebhookHandler<PermissionRevokedEvent>;\r\n  extension_purchased?: WebhookHandler<ExtensionPurchasedEvent>;\r\n  update_user_info?: WebhookHandler<UpdateUserInfoEvent>;\r\n\r\n  // Shop event handlers\r\n  shop_has_order?: WebhookHandler<ShopHasOrderEvent>;\r\n\r\n  // Legacy system event handlers\r\n  add_user_to_tag?: WebhookHandler<AddUserToTagEvent>;\r\n\r\n  // Legacy handlers (for backward compatibility)\r\n  user_click_button?: WebhookHandler<UserClickButtonEvent>;\r\n  oa_send_audio?: WebhookHandler<OASendMessageEvent>;\r\n  oa_send_video?: WebhookHandler<OASendMessageEvent>;\r\n  zns_message_status?: WebhookHandler<ZNSMessageStatusEvent>;\r\n  template_status_update?: WebhookHandler<TemplateStatusEvent>;\r\n  group_user_join?: WebhookHandler<GroupUserJoinEvent>;\r\n  group_user_leave?: WebhookHandler<GroupUserLeaveEvent>;\r\n  group_send_text?: WebhookHandler<GroupSendMessageEvent>;\r\n  group_send_image?: WebhookHandler<GroupSendMessageEvent>;\r\n  group_send_sticker?: WebhookHandler<GroupSendMessageEvent>;\r\n  group_send_file?: WebhookHandler<GroupSendMessageEvent>;\r\n\r\n  // Catch-all handler\r\n  \"*\"?: WebhookHandler<WebhookEvent>;\r\n}\r\n\r\n// ==================== EVENT NAME ENUMS ====================\r\n\r\n/**\r\n * Webhook event names enum\r\n */\r\nexport enum WebhookEventName {\r\n  // User message events\r\n  USER_SEND_LOCATION = \"user_send_location\",\r\n  USER_SEND_IMAGE = \"user_send_image\",\r\n  USER_SEND_LINK = \"user_send_link\",\r\n  USER_SEND_TEXT = \"user_send_text\",\r\n  USER_SEND_STICKER = \"user_send_sticker\",\r\n  USER_SEND_GIF = \"user_send_gif\",\r\n  USER_SEND_AUDIO = \"user_send_audio\",\r\n  USER_SEND_VIDEO = \"user_send_video\",\r\n  USER_SEND_FILE = \"user_send_file\",\r\n  USER_RECEIVED_MESSAGE = \"user_received_message\",\r\n  USER_SEEN_MESSAGE = \"user_seen_message\",\r\n\r\n  // User action events\r\n  FOLLOW = \"follow\",\r\n  UNFOLLOW = \"unfollow\",\r\n  USER_SUBMIT_INFO = \"user_submit_info\",\r\n\r\n  // OA message events\r\n  OA_SEND_TEXT = \"oa_send_text\",\r\n  OA_SEND_IMAGE = \"oa_send_image\",\r\n  OA_SEND_GIF = \"oa_send_gif\",\r\n  OA_SEND_LIST = \"oa_send_list\",\r\n  OA_SEND_FILE = \"oa_send_file\",\r\n  OA_SEND_STICKER = \"oa_send_sticker\",\r\n\r\n  // User interaction events\r\n  USER_CLICK_CHATNOW = \"user_click_chatnow\",\r\n  USER_REACTED_MESSAGE = \"user_reacted_message\",\r\n  USER_REPLY_CONSENT = \"user_reply_consent\",\r\n\r\n  // OA interaction events\r\n  OA_REACTED_MESSAGE = \"oa_reacted_message\",\r\n  OA_SEND_CONSENT = \"oa_send_consent\",\r\n\r\n  // Anonymous user events\r\n  ANONYMOUS_SEND_TEXT = \"anonymous_send_text\",\r\n  ANONYMOUS_SEND_IMAGE = \"anonymous_send_image\",\r\n  ANONYMOUS_SEND_FILE = \"anonymous_send_file\",\r\n  ANONYMOUS_SEND_STICKER = \"anonymous_send_sticker\",\r\n\r\n  // OA anonymous events\r\n  OA_SEND_ANONYMOUS_TEXT = \"oa_send_anonymous_text\",\r\n  OA_SEND_ANONYMOUS_IMAGE = \"oa_send_anonymous_image\",\r\n  OA_SEND_ANONYMOUS_FILE = \"oa_send_anonymous_file\",\r\n  OA_SEND_ANONYMOUS_STICKER = \"oa_send_anonymous_sticker\",\r\n\r\n  // Call events\r\n  OA_CALL_USER = \"oa_call_user\",\r\n  USER_CALL_OA = \"user_call_oa\",\r\n\r\n  // Template and business card events\r\n  OA_SEND_TEMPLATE = \"oa_send_template\",\r\n  USER_SEND_BUSINESS_CARD = \"user_send_business_card\",\r\n\r\n  // Feedback events\r\n  USER_FEEDBACK = \"user_feedback\",\r\n\r\n  // ZNS events\r\n  CHANGE_OA_DAILY_QUOTA = \"change_oa_daily_quota\",\r\n  CHANGE_OA_TEMPLATE_TAGS = \"change_oa_template_tags\",\r\n  CHANGE_TEMPLATE_QUALITY = \"change_template_quality\",\r\n  CHANGE_TEMPLATE_QUOTA = \"change_template_quota\",\r\n  CHANGE_TEMPLATE_STATUS = \"change_template_status\",\r\n  EVENT_JOURNEY_TIME_OUT = \"event_journey_time_out\",\r\n  EVENT_JOURNEY_ACKNOWLEDGED = \"event_journey_acknowledged\",\r\n  ZNS_USER_RECEIVED_MESSAGE = \"user_received_message\",\r\n\r\n  // Group events\r\n  CREATE_GROUP = \"create_group\",\r\n  USER_JOIN_GROUP = \"user_join_group\",\r\n  USER_REQUEST_JOIN_GROUP = \"user_request_join_group\",\r\n  REACT_REQUEST_JOIN_GROUP = \"react_request_join_group\",\r\n  REJECT_REQUEST_JOIN_GROUP = \"reject_request_join_group\",\r\n  ADD_GROUP_ADMIN = \"add_group_admin\",\r\n  REMOVE_GROUP_ADMIN = \"remove_group_admin\",\r\n  UPDATE_GROUP_INFO = \"update_group_info\",\r\n  USER_OUT_GROUP = \"user_out_group\",\r\n  OA_SEND_GROUP_TEXT = \"oa_send_group_text\",\r\n  OA_SEND_GROUP_IMAGE = \"oa_send_group_image\",\r\n  OA_SEND_GROUP_LINK = \"oa_send_group_link\",\r\n  OA_SEND_GROUP_AUDIO = \"oa_send_group_audio\",\r\n  OA_SEND_GROUP_LOCATION = \"oa_send_group_location\",\r\n  OA_SEND_GROUP_VIDEO = \"oa_send_group_video\",\r\n  OA_SEND_GROUP_BUSINESS_CARD = \"oa_send_group_business_card\",\r\n  OA_SEND_GROUP_STICKER = \"oa_send_group_sticker\",\r\n  OA_SEND_GROUP_GIF = \"oa_send_group_gif\",\r\n  OA_SEND_GROUP_FILE = \"oa_send_group_file\",\r\n  USER_SEND_GROUP_TEXT = \"user_send_group_text\",\r\n  USER_SEND_GROUP_IMAGE = \"user_send_group_image\",\r\n  USER_SEND_GROUP_LINK = \"user_send_group_link\",\r\n  USER_SEND_GROUP_AUDIO = \"user_send_group_audio\",\r\n  USER_SEND_GROUP_LOCATION = \"user_send_group_location\",\r\n  USER_SEND_GROUP_VIDEO = \"user_send_group_video\",\r\n  USER_SEND_GROUP_BUSINESS_CARD = \"user_send_group_business_card\",\r\n  USER_SEND_GROUP_STICKER = \"user_send_group_sticker\",\r\n  USER_SEND_GROUP_GIF = \"user_send_group_gif\",\r\n  USER_SEND_GROUP_FILE = \"user_send_group_file\",\r\n\r\n  // Widget events\r\n  WIDGET_INTERACTION_ACCEPTED = \"widget_interaction_accepted\",\r\n  WIDGET_FAILED_TO_SYNC_USER_EXTERNAL_ID = \"widget_failed_to_sync_user_external_id\",\r\n\r\n  // System events\r\n  PERMISSION_REVOKED = \"permission_revoked\",\r\n  EXTENSION_PURCHASED = \"extension_purchased\",\r\n  UPDATE_USER_INFO = \"update_user_info\",\r\n\r\n  // Shop events\r\n  SHOP_HAS_ORDER = \"shop_has_order\",\r\n\r\n  // Legacy system events\r\n  ADD_USER_TO_TAG = \"add_user_to_tag\",\r\n}\r\n\r\n/**\r\n * Attachment types enum\r\n */\r\nexport enum AttachmentType {\r\n  LOCATION = \"location\",\r\n  IMAGE = \"image\",\r\n  LINK = \"link\",\r\n  STICKER = \"sticker\",\r\n  GIF = \"gif\",\r\n  AUDIO = \"audio\",\r\n  VIDEO = \"video\",\r\n  FILE = \"file\",\r\n}\r\n\r\n// ==================== UTILITY TYPES ====================\r\n\r\n/**\r\n * Type guard to check if event is a message event\r\n */\r\nexport function isMessageEvent(\r\n  event: WebhookEvent\r\n): event is MessageWebhookEvent {\r\n  return [\r\n    WebhookEventName.USER_SEND_LOCATION,\r\n    WebhookEventName.USER_SEND_IMAGE,\r\n    WebhookEventName.USER_SEND_LINK,\r\n    WebhookEventName.USER_SEND_TEXT,\r\n    WebhookEventName.USER_SEND_STICKER,\r\n    WebhookEventName.USER_SEND_GIF,\r\n    WebhookEventName.USER_SEND_AUDIO,\r\n    WebhookEventName.USER_SEND_VIDEO,\r\n    WebhookEventName.USER_SEND_FILE,\r\n    WebhookEventName.USER_RECEIVED_MESSAGE,\r\n    WebhookEventName.USER_SEEN_MESSAGE,\r\n    WebhookEventName.OA_SEND_TEXT,\r\n    WebhookEventName.OA_SEND_IMAGE,\r\n    WebhookEventName.OA_SEND_GIF,\r\n    WebhookEventName.OA_SEND_LIST,\r\n    WebhookEventName.OA_SEND_FILE,\r\n    WebhookEventName.OA_SEND_STICKER,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_TEXT,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_IMAGE,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_FILE,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_STICKER,\r\n    WebhookEventName.OA_SEND_TEMPLATE,\r\n    WebhookEventName.ANONYMOUS_SEND_TEXT,\r\n    WebhookEventName.ANONYMOUS_SEND_IMAGE,\r\n    WebhookEventName.ANONYMOUS_SEND_FILE,\r\n    WebhookEventName.ANONYMOUS_SEND_STICKER,\r\n    WebhookEventName.USER_SEND_BUSINESS_CARD,\r\n    WebhookEventName.OA_SEND_GROUP_TEXT,\r\n    WebhookEventName.OA_SEND_GROUP_IMAGE,\r\n    WebhookEventName.OA_SEND_GROUP_LINK,\r\n    WebhookEventName.OA_SEND_GROUP_AUDIO,\r\n    WebhookEventName.OA_SEND_GROUP_LOCATION,\r\n    WebhookEventName.OA_SEND_GROUP_VIDEO,\r\n    WebhookEventName.OA_SEND_GROUP_BUSINESS_CARD,\r\n    WebhookEventName.OA_SEND_GROUP_STICKER,\r\n    WebhookEventName.OA_SEND_GROUP_GIF,\r\n    WebhookEventName.OA_SEND_GROUP_FILE,\r\n    WebhookEventName.USER_SEND_GROUP_TEXT,\r\n    WebhookEventName.USER_SEND_GROUP_LINK,\r\n    WebhookEventName.USER_SEND_GROUP_AUDIO,\r\n    WebhookEventName.USER_SEND_GROUP_LOCATION,\r\n    WebhookEventName.USER_SEND_GROUP_VIDEO,\r\n    WebhookEventName.USER_SEND_GROUP_BUSINESS_CARD,\r\n    WebhookEventName.USER_SEND_GROUP_STICKER,\r\n    WebhookEventName.USER_SEND_GROUP_GIF,\r\n    WebhookEventName.USER_SEND_GROUP_FILE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a follow/unfollow event\r\n */\r\nexport function isFollowEvent(\r\n  event: WebhookEvent\r\n): event is UserFollowEvent | UserUnfollowEvent {\r\n  return [WebhookEventName.FOLLOW, WebhookEventName.UNFOLLOW].includes(\r\n    event.event_name as WebhookEventName\r\n  );\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a user action event\r\n */\r\nexport function isUserActionEvent(\r\n  event: WebhookEvent\r\n): event is UserActionWebhookEvent {\r\n  return [\r\n    WebhookEventName.FOLLOW,\r\n    WebhookEventName.UNFOLLOW,\r\n    WebhookEventName.USER_SUBMIT_INFO,\r\n    WebhookEventName.USER_CLICK_CHATNOW,\r\n    WebhookEventName.USER_REACTED_MESSAGE,\r\n    WebhookEventName.USER_REPLY_CONSENT,\r\n    WebhookEventName.USER_SEND_BUSINESS_CARD,\r\n    WebhookEventName.USER_FEEDBACK,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a shop event\r\n */\r\nexport function isShopEvent(event: WebhookEvent): event is ShopWebhookEvent {\r\n  return event.event_name === WebhookEventName.SHOP_HAS_ORDER;\r\n}\r\n\r\n/**\r\n * Type guard to check if event is an OA message event\r\n */\r\nexport function isOAMessageEvent(\r\n  event: WebhookEvent\r\n): event is OAMessageWebhookEvent {\r\n  return [\r\n    WebhookEventName.OA_SEND_TEXT,\r\n    WebhookEventName.OA_SEND_IMAGE,\r\n    WebhookEventName.OA_SEND_GIF,\r\n    WebhookEventName.OA_SEND_LIST,\r\n    WebhookEventName.OA_SEND_FILE,\r\n    WebhookEventName.OA_SEND_STICKER,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_TEXT,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_IMAGE,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_FILE,\r\n    WebhookEventName.OA_SEND_ANONYMOUS_STICKER,\r\n    WebhookEventName.OA_SEND_TEMPLATE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is an anonymous user event\r\n */\r\nexport function isAnonymousEvent(\r\n  event: WebhookEvent\r\n): event is AnonymousWebhookEvent {\r\n  return [\r\n    WebhookEventName.ANONYMOUS_SEND_TEXT,\r\n    WebhookEventName.ANONYMOUS_SEND_IMAGE,\r\n    WebhookEventName.ANONYMOUS_SEND_FILE,\r\n    WebhookEventName.ANONYMOUS_SEND_STICKER,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a call event\r\n */\r\nexport function isCallEvent(event: WebhookEvent): event is CallWebhookEvent {\r\n  return [\r\n    WebhookEventName.OA_CALL_USER,\r\n    WebhookEventName.USER_CALL_OA,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a template event\r\n */\r\nexport function isTemplateEvent(\r\n  event: WebhookEvent\r\n): event is OASendTemplateEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_TEMPLATE;\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a business card event\r\n */\r\nexport function isBusinessCardEvent(\r\n  event: WebhookEvent\r\n): event is UserSendBusinessCardEvent {\r\n  return event.event_name === WebhookEventName.USER_SEND_BUSINESS_CARD;\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a feedback event\r\n */\r\nexport function isFeedbackEvent(\r\n  event: WebhookEvent\r\n): event is UserFeedbackEvent {\r\n  return event.event_name === WebhookEventName.USER_FEEDBACK;\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a ZNS event\r\n */\r\nexport function isZNSEvent(event: WebhookEvent): event is ZNSWebhookEvent {\r\n  return [\r\n    WebhookEventName.CHANGE_OA_DAILY_QUOTA,\r\n    WebhookEventName.CHANGE_OA_TEMPLATE_TAGS,\r\n    WebhookEventName.CHANGE_TEMPLATE_QUALITY,\r\n    WebhookEventName.CHANGE_TEMPLATE_QUOTA,\r\n    WebhookEventName.CHANGE_TEMPLATE_STATUS,\r\n    WebhookEventName.EVENT_JOURNEY_TIME_OUT,\r\n    WebhookEventName.EVENT_JOURNEY_ACKNOWLEDGED,\r\n    WebhookEventName.ZNS_USER_RECEIVED_MESSAGE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a group event\r\n */\r\nexport function isGroupEvent(event: WebhookEvent): event is GroupWebhookEvent {\r\n  return [\r\n    WebhookEventName.CREATE_GROUP,\r\n    WebhookEventName.USER_JOIN_GROUP,\r\n    WebhookEventName.USER_REQUEST_JOIN_GROUP,\r\n    WebhookEventName.REACT_REQUEST_JOIN_GROUP,\r\n    WebhookEventName.REJECT_REQUEST_JOIN_GROUP,\r\n    WebhookEventName.ADD_GROUP_ADMIN,\r\n    WebhookEventName.REMOVE_GROUP_ADMIN,\r\n    WebhookEventName.UPDATE_GROUP_INFO,\r\n    WebhookEventName.USER_OUT_GROUP,\r\n    WebhookEventName.OA_SEND_GROUP_TEXT,\r\n    WebhookEventName.OA_SEND_GROUP_IMAGE,\r\n    WebhookEventName.OA_SEND_GROUP_LINK,\r\n    WebhookEventName.OA_SEND_GROUP_AUDIO,\r\n    WebhookEventName.OA_SEND_GROUP_LOCATION,\r\n    WebhookEventName.OA_SEND_GROUP_VIDEO,\r\n    WebhookEventName.OA_SEND_GROUP_BUSINESS_CARD,\r\n    WebhookEventName.OA_SEND_GROUP_STICKER,\r\n    WebhookEventName.OA_SEND_GROUP_GIF,\r\n    WebhookEventName.OA_SEND_GROUP_FILE,\r\n    WebhookEventName.USER_SEND_GROUP_TEXT,\r\n    WebhookEventName.USER_SEND_GROUP_LINK,\r\n    WebhookEventName.USER_SEND_GROUP_AUDIO,\r\n    WebhookEventName.USER_SEND_GROUP_LOCATION,\r\n    WebhookEventName.USER_SEND_GROUP_VIDEO,\r\n    WebhookEventName.USER_SEND_GROUP_BUSINESS_CARD,\r\n    WebhookEventName.USER_SEND_GROUP_STICKER,\r\n    WebhookEventName.USER_SEND_GROUP_GIF,\r\n    WebhookEventName.USER_SEND_GROUP_FILE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a journey event\r\n */\r\nexport function isJourneyEvent(\r\n  event: WebhookEvent\r\n): event is JourneyTimeoutEvent | JourneyAcknowledgedEvent {\r\n  return [\r\n    WebhookEventName.EVENT_JOURNEY_TIME_OUT,\r\n    WebhookEventName.EVENT_JOURNEY_ACKNOWLEDGED,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a quota/template change event\r\n */\r\nexport function isQuotaTemplateEvent(\r\n  event: WebhookEvent\r\n): event is\r\n  | ChangeOADailyQuotaEvent\r\n  | ChangeOATemplateTagsEvent\r\n  | ChangeTemplateQualityEvent\r\n  | ChangeTemplateQuotaEvent\r\n  | ChangeTemplateStatusEvent {\r\n  return [\r\n    WebhookEventName.CHANGE_OA_DAILY_QUOTA,\r\n    WebhookEventName.CHANGE_OA_TEMPLATE_TAGS,\r\n    WebhookEventName.CHANGE_TEMPLATE_QUALITY,\r\n    WebhookEventName.CHANGE_TEMPLATE_QUOTA,\r\n    WebhookEventName.CHANGE_TEMPLATE_STATUS,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a widget event\r\n */\r\nexport function isWidgetEvent(\r\n  event: WebhookEvent\r\n): event is WidgetWebhookEvent {\r\n  return [\r\n    WebhookEventName.WIDGET_INTERACTION_ACCEPTED,\r\n    WebhookEventName.WIDGET_FAILED_TO_SYNC_USER_EXTERNAL_ID,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a system event\r\n */\r\nexport function isSystemEvent(\r\n  event: WebhookEvent\r\n): event is SystemWebhookEvent {\r\n  return [\r\n    WebhookEventName.ADD_USER_TO_TAG,\r\n    WebhookEventName.PERMISSION_REVOKED,\r\n    WebhookEventName.EXTENSION_PURCHASED,\r\n    WebhookEventName.UPDATE_USER_INFO,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a permission event\r\n */\r\nexport function isPermissionEvent(\r\n  event: WebhookEvent\r\n): event is PermissionRevokedEvent {\r\n  return event.event_name === WebhookEventName.PERMISSION_REVOKED;\r\n}\r\n\r\n/**\r\n * Type guard to check if event is an extension event\r\n */\r\nexport function isExtensionEvent(\r\n  event: WebhookEvent\r\n): event is ExtensionPurchasedEvent {\r\n  return event.event_name === WebhookEventName.EXTENSION_PURCHASED;\r\n}\r\n\r\n/**\r\n * Type guard to check if event is an update user info event\r\n */\r\nexport function isUpdateUserInfoEvent(\r\n  event: WebhookEvent\r\n): event is UpdateUserInfoEvent {\r\n  return event.event_name === WebhookEventName.UPDATE_USER_INFO;\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a reaction event\r\n */\r\nexport function isReactionEvent(\r\n  event: WebhookEvent\r\n): event is UserReactedMessageEvent | OAReactedMessageEvent {\r\n  return [\r\n    WebhookEventName.USER_REACTED_MESSAGE,\r\n    WebhookEventName.OA_REACTED_MESSAGE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a consent event\r\n */\r\nexport function isConsentEvent(\r\n  event: WebhookEvent\r\n): event is OASendConsentEvent | UserReplyConsentEvent {\r\n  return [\r\n    WebhookEventName.OA_SEND_CONSENT,\r\n    WebhookEventName.USER_REPLY_CONSENT,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if message has attachments\r\n */\r\nexport function hasAttachments(\r\n  message: WebhookBaseMessage\r\n): message is WebhookMessageWithAttachments {\r\n  return (\r\n    \"attachments\" in message &&\r\n    Array.isArray((message as WebhookMessageWithAttachments).attachments)\r\n  );\r\n}\r\n\r\n// ==================== MESSAGE DIRECTION & TARGET TYPE GUARDS ====================\r\n\r\n/**\r\n * Type guard to check if event is a user message (from individual user to OA)\r\n */\r\nexport function isUserMessageEvent(\r\n  event: WebhookEvent\r\n): event is UserMessageWebhookEvent {\r\n  return [\r\n    WebhookEventName.USER_SEND_LOCATION,\r\n    WebhookEventName.USER_SEND_IMAGE,\r\n    WebhookEventName.USER_SEND_LINK,\r\n    WebhookEventName.USER_SEND_TEXT,\r\n    WebhookEventName.USER_SEND_STICKER,\r\n    WebhookEventName.USER_SEND_GIF,\r\n    WebhookEventName.USER_SEND_AUDIO,\r\n    WebhookEventName.USER_SEND_VIDEO,\r\n    WebhookEventName.USER_SEND_FILE,\r\n    WebhookEventName.USER_RECEIVED_MESSAGE,\r\n    WebhookEventName.USER_SEEN_MESSAGE,\r\n    WebhookEventName.USER_SEND_BUSINESS_CARD,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is a group message (from user in group)\r\n */\r\nexport function isGroupMessageEvent(\r\n  event: WebhookEvent\r\n): event is GroupWebhookEvent {\r\n  return [\r\n    WebhookEventName.USER_SEND_GROUP_TEXT,\r\n    WebhookEventName.USER_SEND_GROUP_LINK,\r\n    WebhookEventName.USER_SEND_GROUP_AUDIO,\r\n    WebhookEventName.USER_SEND_GROUP_LOCATION,\r\n    WebhookEventName.USER_SEND_GROUP_VIDEO,\r\n    WebhookEventName.USER_SEND_GROUP_BUSINESS_CARD,\r\n    WebhookEventName.USER_SEND_GROUP_STICKER,\r\n    WebhookEventName.USER_SEND_GROUP_GIF,\r\n    WebhookEventName.USER_SEND_GROUP_FILE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is OA sending message to individual user\r\n */\r\nexport function isOAToUserMessageEvent(\r\n  event: WebhookEvent\r\n): event is OAMessageWebhookEvent {\r\n  return [\r\n    WebhookEventName.OA_SEND_TEXT,\r\n    WebhookEventName.OA_SEND_IMAGE,\r\n    WebhookEventName.OA_SEND_GIF,\r\n    WebhookEventName.OA_SEND_LIST,\r\n    WebhookEventName.OA_SEND_FILE,\r\n    WebhookEventName.OA_SEND_STICKER,\r\n    WebhookEventName.OA_SEND_TEMPLATE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Type guard to check if event is OA sending message to group\r\n */\r\nexport function isOAToGroupMessageEvent(\r\n  event: WebhookEvent\r\n): event is GroupWebhookEvent {\r\n  return [\r\n    WebhookEventName.OA_SEND_GROUP_TEXT,\r\n    WebhookEventName.OA_SEND_GROUP_IMAGE,\r\n    WebhookEventName.OA_SEND_GROUP_LINK,\r\n    WebhookEventName.OA_SEND_GROUP_AUDIO,\r\n    WebhookEventName.OA_SEND_GROUP_LOCATION,\r\n    WebhookEventName.OA_SEND_GROUP_VIDEO,\r\n    WebhookEventName.OA_SEND_GROUP_BUSINESS_CARD,\r\n    WebhookEventName.OA_SEND_GROUP_STICKER,\r\n    WebhookEventName.OA_SEND_GROUP_GIF,\r\n    WebhookEventName.OA_SEND_GROUP_FILE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Helper function to get message direction and target type\r\n */\r\nexport function getMessageDirection(event: WebhookEvent): {\r\n  direction: 'incoming' | 'outgoing' | 'unknown';\r\n  target: 'user' | 'group' | 'unknown';\r\n  description: string;\r\n} {\r\n  if (isUserMessageEvent(event)) {\r\n    return {\r\n      direction: 'incoming',\r\n      target: 'user',\r\n      description: 'Tin nhắn từ người dùng cá nhân gửi tới OA'\r\n    };\r\n  }\r\n\r\n  if (isGroupMessageEvent(event)) {\r\n    return {\r\n      direction: 'incoming',\r\n      target: 'group',\r\n      description: 'Tin nhắn từ người dùng trong group'\r\n    };\r\n  }\r\n\r\n  if (isOAToUserMessageEvent(event)) {\r\n    return {\r\n      direction: 'outgoing',\r\n      target: 'user',\r\n      description: 'Tin nhắn từ OA gửi tới người dùng cá nhân'\r\n    };\r\n  }\r\n\r\n  if (isOAToGroupMessageEvent(event)) {\r\n    return {\r\n      direction: 'outgoing',\r\n      target: 'group',\r\n      description: 'Tin nhắn từ OA gửi tới group'\r\n    };\r\n  }\r\n\r\n  return {\r\n    direction: 'unknown',\r\n    target: 'unknown',\r\n    description: 'Loại sự kiện không xác định'\r\n  };\r\n}\r\n\r\nexport interface WebhookEventIdentifiers {\r\n  oaId: string | null;\r\n  appId: string | null;\r\n  senderId: string | null;\r\n  recipientId: string | null;\r\n}\r\n\r\nfunction readWebhookString(value: unknown): string | null {\r\n  if (typeof value !== \"string\") {\r\n    return null;\r\n  }\r\n\r\n  const normalizedValue = value.trim();\r\n  return normalizedValue.length > 0 ? normalizedValue : null;\r\n}\r\n\r\nfunction readWebhookObjectId(value: unknown): string | null {\r\n  if (!value || typeof value !== \"object\") {\r\n    return null;\r\n  }\r\n\r\n  return readWebhookString((value as { id?: unknown }).id);\r\n}\r\n\r\nfunction hasWebhookEventPrefix(event: WebhookEvent, prefix: string): boolean {\r\n  return event.event_name.startsWith(prefix);\r\n}\r\n\r\nexport function getWebhookEventAppId(event: WebhookEvent): string | null {\r\n  const directAppId = readWebhookString((event as { app_id?: unknown }).app_id);\r\n\r\n  if (directAppId) {\r\n    return directAppId;\r\n  }\r\n\r\n  if (\"message\" in event && event.message && typeof event.message === \"object\") {\r\n    const messageSource = (event.message as { source?: unknown }).source;\r\n\r\n    if (messageSource && typeof messageSource === \"object\") {\r\n      return readWebhookString(\r\n        (messageSource as { app_id?: unknown }).app_id\r\n      );\r\n    }\r\n  }\r\n\r\n  return null;\r\n}\r\n\r\nexport function getWebhookEventOaId(event: WebhookEvent): string | null {\r\n  const directOaId = readWebhookString((event as { oa_id?: unknown }).oa_id);\r\n\r\n  if (directOaId) {\r\n    return directOaId;\r\n  }\r\n\r\n  if (hasWebhookEventPrefix(event, \"oa_\")) {\r\n    return readWebhookObjectId((event as { sender?: unknown }).sender);\r\n  }\r\n\r\n  if (\r\n    hasWebhookEventPrefix(event, \"user_\") ||\r\n    hasWebhookEventPrefix(event, \"anonymous_\")\r\n  ) {\r\n    return readWebhookObjectId((event as { recipient?: unknown }).recipient);\r\n  }\r\n\r\n  return null;\r\n}\r\n\r\nexport function getWebhookEventSenderId(event: WebhookEvent): string | null {\r\n  const directSenderId = readWebhookObjectId(\r\n    (event as { sender?: unknown }).sender\r\n  );\r\n\r\n  if (directSenderId) {\r\n    return directSenderId;\r\n  }\r\n\r\n  const fallbackSenderId =\r\n    readWebhookString((event as { user_id?: unknown }).user_id) ??\r\n    readWebhookObjectId((event as { follower?: unknown }).follower) ??\r\n    readWebhookObjectId((event as { customer?: unknown }).customer) ??\r\n    readWebhookObjectId((event as { user?: unknown }).user);\r\n\r\n  if (fallbackSenderId) {\r\n    return fallbackSenderId;\r\n  }\r\n\r\n  if (hasWebhookEventPrefix(event, \"oa_\")) {\r\n    return getWebhookEventOaId(event);\r\n  }\r\n\r\n  return null;\r\n}\r\n\r\nexport function getWebhookEventRecipientId(\r\n  event: WebhookEvent\r\n): string | null {\r\n  const directRecipientId = readWebhookObjectId(\r\n    (event as { recipient?: unknown }).recipient\r\n  );\r\n\r\n  if (directRecipientId) {\r\n    return directRecipientId;\r\n  }\r\n\r\n  if (hasWebhookEventPrefix(event, \"oa_\")) {\r\n    return readWebhookString((event as { user_id?: unknown }).user_id);\r\n  }\r\n\r\n  return getWebhookEventOaId(event);\r\n}\r\n\r\nexport function getWebhookEventIdentifiers(\r\n  event: WebhookEvent\r\n): WebhookEventIdentifiers {\r\n  return {\r\n    oaId: getWebhookEventOaId(event),\r\n    appId: getWebhookEventAppId(event),\r\n    senderId: getWebhookEventSenderId(event),\r\n    recipientId: getWebhookEventRecipientId(event),\r\n  };\r\n}\r\n","/**\r\n * Type guard utilities for Zalo SDK webhook events\r\n * \r\n * This file provides type-safe functions to check webhook event types\r\n * and narrow down TypeScript types for better development experience.\r\n */\r\n\r\nimport {\r\n  WebhookEvent,\r\n  GroupWebhookEvent,\r\n  OAMessageWebhookEvent,\r\n  UserMessageWebhookEvent,\r\n  UserSendGroupTextEvent,\r\n  UserSendGroupImageEvent,\r\n  UserSendGroupVideoEvent,\r\n  UserSendGroupAudioEvent,\r\n  UserSendGroupFileEvent,\r\n  OASendTextEvent,\r\n  OASendImageEvent,\r\n  OASendFileEvent,\r\n  OASendStickerEvent,\r\n  OASendGifEvent,\r\n  OASendGroupTextEvent,\r\n  OASendGroupImageEvent,\r\n  OASendGroupFileEvent,\r\n  OASendGroupStickerEvent,\r\n  OASendGroupGifEvent,\r\n  WebhookEventName,\r\n} from '../types/webhook';\r\n\r\n// ===== USER GROUP MESSAGE TYPE GUARDS =====\r\n\r\n/**\r\n * Type union for user message events in groups\r\n */\r\nexport type UserGroupMessageEvent =\r\n  | UserSendGroupTextEvent\r\n  | UserSendGroupImageEvent\r\n  | UserSendGroupVideoEvent\r\n  | UserSendGroupAudioEvent\r\n  | UserSendGroupFileEvent;\r\n\r\n/**\r\n * Check if event is a user sending text message to group\r\n */\r\nexport function isUserSendGroupTextEvent(\r\n  event: WebhookEvent\r\n): event is UserSendGroupTextEvent {\r\n  return event.event_name === WebhookEventName.USER_SEND_GROUP_TEXT;\r\n}\r\n\r\n/**\r\n * Check if event is a user sending image message to group\r\n */\r\nexport function isUserSendGroupImageEvent(\r\n  event: WebhookEvent\r\n): event is UserSendGroupImageEvent {\r\n  return event.event_name === WebhookEventName.USER_SEND_GROUP_IMAGE;\r\n}\r\n\r\n/**\r\n * Check if event is a user sending video message to group\r\n */\r\nexport function isUserSendGroupVideoEvent(\r\n  event: WebhookEvent\r\n): event is UserSendGroupVideoEvent {\r\n  return event.event_name === WebhookEventName.USER_SEND_GROUP_VIDEO;\r\n}\r\n\r\n/**\r\n * Check if event is a user sending audio message to group\r\n */\r\nexport function isUserSendGroupAudioEvent(\r\n  event: WebhookEvent\r\n): event is UserSendGroupAudioEvent {\r\n  return event.event_name === WebhookEventName.USER_SEND_GROUP_AUDIO;\r\n}\r\n\r\n/**\r\n * Check if event is a user sending file to group\r\n */\r\nexport function isUserSendGroupFileEvent(\r\n  event: WebhookEvent\r\n): event is UserSendGroupFileEvent {\r\n  return event.event_name === WebhookEventName.USER_SEND_GROUP_FILE;\r\n}\r\n\r\n/**\r\n * Check if event is any user group message event\r\n */\r\nexport function isUserGroupMessageEvent(\r\n  event: WebhookEvent\r\n): event is UserGroupMessageEvent {\r\n  return [\r\n    WebhookEventName.USER_SEND_GROUP_TEXT,\r\n    WebhookEventName.USER_SEND_GROUP_IMAGE,\r\n    WebhookEventName.USER_SEND_GROUP_VIDEO,\r\n    WebhookEventName.USER_SEND_GROUP_AUDIO,\r\n    WebhookEventName.USER_SEND_GROUP_FILE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n// ===== OA MESSAGE TYPE GUARDS =====\r\n\r\n/**\r\n * Check if event is OA sending text message to user\r\n */\r\nexport function isOASendTextEvent(\r\n  event: WebhookEvent\r\n): event is OASendTextEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_TEXT;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending image message to user\r\n */\r\nexport function isOASendImageEvent(\r\n  event: WebhookEvent\r\n): event is OASendImageEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_IMAGE;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending file to user\r\n */\r\nexport function isOASendFileEvent(\r\n  event: WebhookEvent\r\n): event is OASendFileEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_FILE;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending sticker to user\r\n */\r\nexport function isOASendStickerEvent(\r\n  event: WebhookEvent\r\n): event is OASendStickerEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_STICKER;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending GIF to user\r\n */\r\nexport function isOASendGifEvent(\r\n  event: WebhookEvent\r\n): event is OASendGifEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_GIF;\r\n}\r\n\r\n// ===== OA GROUP MESSAGE TYPE GUARDS =====\r\n\r\n/**\r\n * Check if event is OA sending text message to group\r\n */\r\nexport function isOASendGroupTextEvent(\r\n  event: WebhookEvent\r\n): event is OASendGroupTextEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_GROUP_TEXT;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending image to group\r\n */\r\nexport function isOASendGroupImageEvent(\r\n  event: WebhookEvent\r\n): event is OASendGroupImageEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_GROUP_IMAGE;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending file to group\r\n */\r\nexport function isOASendGroupFileEvent(\r\n  event: WebhookEvent\r\n): event is OASendGroupFileEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_GROUP_FILE;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending sticker to group\r\n */\r\nexport function isOASendGroupStickerEvent(\r\n  event: WebhookEvent\r\n): event is OASendGroupStickerEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_GROUP_STICKER;\r\n}\r\n\r\n/**\r\n * Check if event is OA sending GIF to group\r\n */\r\nexport function isOASendGroupGifEvent(\r\n  event: WebhookEvent\r\n): event is OASendGroupGifEvent {\r\n  return event.event_name === WebhookEventName.OA_SEND_GROUP_GIF;\r\n}\r\n\r\n// ===== COMBINED TYPE GUARDS =====\r\n\r\n/**\r\n * Check if event is any OA message event (personal or group)\r\n */\r\nexport function isOAMessageEvent(\r\n  event: WebhookEvent\r\n): event is OAMessageWebhookEvent {\r\n  return [\r\n    WebhookEventName.OA_SEND_TEXT,\r\n    WebhookEventName.OA_SEND_IMAGE,\r\n    WebhookEventName.OA_SEND_FILE,\r\n    WebhookEventName.OA_SEND_STICKER,\r\n    WebhookEventName.OA_SEND_GIF,\r\n    WebhookEventName.OA_SEND_GROUP_TEXT,\r\n    WebhookEventName.OA_SEND_GROUP_IMAGE,\r\n    WebhookEventName.OA_SEND_GROUP_FILE,\r\n    WebhookEventName.OA_SEND_GROUP_STICKER,\r\n    WebhookEventName.OA_SEND_GROUP_GIF,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Check if event is any user message event (personal or group)\r\n */\r\nexport function isUserMessageEvent(\r\n  event: WebhookEvent\r\n): event is UserMessageWebhookEvent {\r\n  return [\r\n    WebhookEventName.USER_SEND_TEXT,\r\n    WebhookEventName.USER_SEND_IMAGE,\r\n    WebhookEventName.USER_SEND_AUDIO,\r\n    WebhookEventName.USER_SEND_VIDEO,\r\n    WebhookEventName.USER_SEND_FILE,\r\n    WebhookEventName.USER_SEND_STICKER,\r\n    WebhookEventName.USER_SEND_LOCATION,\r\n    WebhookEventName.USER_SEND_GROUP_TEXT,\r\n    WebhookEventName.USER_SEND_GROUP_VIDEO,\r\n    WebhookEventName.USER_SEND_GROUP_AUDIO,\r\n    WebhookEventName.USER_SEND_GROUP_FILE,\r\n  ].includes(event.event_name as WebhookEventName);\r\n}\r\n\r\n/**\r\n * Check if event has message attachments\r\n */\r\nexport function hasAttachments(event: WebhookEvent): boolean {\r\n  if ('message' in event && event.message && 'attachments' in event.message) {\r\n    return Array.isArray(event.message.attachments) && event.message.attachments.length > 0;\r\n  }\r\n  return false;\r\n}\r\n\r\n/**\r\n * Check if event is from a group context\r\n */\r\nexport function isFromGroup(event: WebhookEvent): boolean {\r\n  return 'group_id' in event || event.event_name.includes('group');\r\n}\r\n\r\n/**\r\n * Check if event is from a personal context\r\n */\r\nexport function isFromPersonal(event: WebhookEvent): boolean {\r\n  return !isFromGroup(event);\r\n}\r\n","/**\r\n * Base HTTP client for Zalo API\r\n */\r\n\r\nimport axios, {\r\n  AxiosInstance,\r\n  AxiosRequestConfig,\r\n  AxiosResponse,\r\n  AxiosError,\r\n} from \"axios\";\r\nimport FormData from \"form-data\";\r\nimport {\r\n  ZaloSDKConfig,\r\n  ZaloSDKError,\r\n  Logger,\r\n  ConsoleLogger,\r\n  RequestConfig,\r\n} from \"../types/common\";\r\n\r\n/**\r\n * Base client for making HTTP requests to Zalo API\r\n */\r\nexport class BaseClient {\r\n  protected readonly axios: AxiosInstance;\r\n  protected readonly logger: Logger;\r\n  protected readonly config: Required<ZaloSDKConfig>;\r\n\r\n  constructor(config: ZaloSDKConfig) {\r\n    // Set default configuration\r\n    this.config = {\r\n      appId: config.appId,\r\n      appSecret: config.appSecret,\r\n      timeout: config.timeout || 30000,\r\n      debug: config.debug || false,\r\n      apiBaseUrl: config.apiBaseUrl || \"https://openapi.zalo.me\",\r\n      retry: {\r\n        attempts: config.retry?.attempts || 3,\r\n        delay: config.retry?.delay || 1000,\r\n        ...config.retry,\r\n      },\r\n    };\r\n\r\n    this.logger = new ConsoleLogger(this.config.debug);\r\n\r\n    // Create axios instance\r\n    this.axios = axios.create({\r\n      baseURL: this.config.apiBaseUrl,\r\n      timeout: this.config.timeout,\r\n      headers: {\r\n        \"Content-Type\": \"application/json\",\r\n        \"User-Agent\": \"RedAI-Zalo-SDK/1.0.0\",\r\n      },\r\n    });\r\n\r\n    this.setupInterceptors();\r\n  }\r\n\r\n  /**\r\n   * Setup axios interceptors for logging and error handling\r\n   */\r\n  private setupInterceptors(): void {\r\n    // Request interceptor\r\n    this.axios.interceptors.request.use(\r\n      (config: any) => {\r\n        this.logger.debug(\r\n          `Making ${config.method?.toUpperCase()} request to ${config.url}`,\r\n          {\r\n            headers: this.sanitizeHeaders(config.headers || {}),\r\n            params: config.params,\r\n          }\r\n        );\r\n        return config;\r\n      },\r\n      (error: any) => {\r\n        this.logger.error(\"Request setup error:\", error.message);\r\n        return Promise.reject(error);\r\n      }\r\n    );\r\n\r\n    // Response interceptor\r\n    this.axios.interceptors.response.use(\r\n      (response: AxiosResponse) => {\r\n        this.logger.debug(`Received response from ${response.config.url}`, {\r\n          status: response.status,\r\n          data: response.data,\r\n        });\r\n        return response;\r\n      },\r\n      (error: AxiosError) => {\r\n        this.handleAxiosError(error);\r\n        return Promise.reject(error);\r\n      }\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Sanitize headers for logging (remove sensitive information)\r\n   */\r\n  private sanitizeHeaders(headers: Record<string, any>): Record<string, any> {\r\n    const sanitized = { ...headers };\r\n    if (sanitized.access_token) {\r\n      sanitized.access_token = \"***REDACTED***\";\r\n    }\r\n    if (sanitized.secret_key) {\r\n      sanitized.secret_key = \"***REDACTED***\";\r\n    }\r\n    return sanitized;\r\n  }\r\n\r\n  /**\r\n   * Handle axios errors and log them\r\n   */\r\n  private handleAxiosError(error: AxiosError): void {\r\n    if (error.response) {\r\n      this.logger.error(`HTTP ${error.response.status} error:`, {\r\n        url: error.config?.url,\r\n        status: error.response.status,\r\n        statusText: error.response.statusText,\r\n        data: error.response.data,\r\n      });\r\n    } else if (error.request) {\r\n      this.logger.error(\"No response received:\", {\r\n        url: error.config?.url,\r\n        message: error.message,\r\n      });\r\n    } else {\r\n      this.logger.error(\"Request setup error:\", error.message);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Make a GET request\r\n   */\r\n  protected async get<T = any>(\r\n    url: string,\r\n    accessToken?: string,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method: \"GET\",\r\n      url,\r\n      headers: accessToken ? { access_token: accessToken } : {},\r\n      params,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Make a POST request\r\n   */\r\n  protected async post<T = any>(\r\n    url: string,\r\n    accessToken?: string,\r\n    data?: any,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method: \"POST\",\r\n      url,\r\n      headers: accessToken ? { access_token: accessToken } : {},\r\n      data,\r\n      params,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Make a PUT request\r\n   */\r\n  protected async put<T = any>(\r\n    url: string,\r\n    accessToken?: string,\r\n    data?: any,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method: \"PUT\",\r\n      url,\r\n      headers: accessToken ? { access_token: accessToken } : {},\r\n      data,\r\n      params,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Make a DELETE request\r\n   */\r\n  protected async delete<T = any>(\r\n    url: string,\r\n    accessToken?: string,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method: \"DELETE\",\r\n      url,\r\n      headers: accessToken ? { access_token: accessToken } : {},\r\n      params,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Upload file using FormData\r\n   */\r\n  protected async uploadFile<T = any>(\r\n    url: string,\r\n    accessToken: string,\r\n    file: Buffer | NodeJS.ReadableStream,\r\n    filename: string,\r\n    additionalFields?: Record<string, any>\r\n  ): Promise<T> {\r\n    const formData = new FormData();\r\n    formData.append(\"file\", file, filename);\r\n\r\n    if (additionalFields) {\r\n      Object.entries(additionalFields).forEach(([key, value]) => {\r\n        formData.append(key, value);\r\n      });\r\n    }\r\n\r\n    return this.request<T>({\r\n      method: \"POST\",\r\n      url,\r\n      headers: {\r\n        access_token: accessToken,\r\n        ...formData.getHeaders(), // Let form-data package set Content-Type with proper boundary\r\n      },\r\n      data: formData,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Make a generic HTTP request with retry logic\r\n   */\r\n  protected async request<T = any>(config: RequestConfig): Promise<T> {\r\n    let lastError: Error;\r\n\r\n    for (\r\n      let attempt = 1;\r\n      attempt <= (this.config.retry?.attempts || 3);\r\n      attempt++\r\n    ) {\r\n      try {\r\n        const axiosConfig: AxiosRequestConfig = {\r\n          method: config.method,\r\n          url: config.url,\r\n          headers: config.headers,\r\n          params: config.params,\r\n          data: config.data,\r\n          timeout: config.timeout || this.config.timeout,\r\n        };\r\n\r\n        const response: AxiosResponse<T> = await this.axios.request(\r\n          axiosConfig\r\n        );\r\n\r\n        // Check for Zalo API errors in response\r\n        this.validateZaloResponse(response.data);\r\n\r\n        return response.data;\r\n      } catch (error) {\r\n        lastError = error as Error;\r\n\r\n        if (\r\n          attempt < (this.config.retry?.attempts || 3) &&\r\n          this.shouldRetry(error as AxiosError)\r\n        ) {\r\n          this.logger.warn(\r\n            `Request failed, retrying (${attempt}/${\r\n              this.config.retry?.attempts || 3\r\n            })...`\r\n          );\r\n          await this.delay((this.config.retry?.delay || 1000) * attempt);\r\n          continue;\r\n        }\r\n\r\n        break;\r\n      }\r\n    }\r\n\r\n    throw this.createSDKError(lastError!);\r\n  }\r\n\r\n  /**\r\n   * Validate Zalo API response for errors\r\n   */\r\n  private validateZaloResponse(data: any): void {\r\n    if (data && typeof data === \"object\") {\r\n      // Check for standard Zalo error format\r\n      if (\"error\" in data && data.error !== 0) {\r\n        const errorMessage =\r\n          data.error_description || data.message || \"Unknown Zalo API error\";\r\n        throw new ZaloSDKError(errorMessage, data.error, data);\r\n      }\r\n\r\n      // Check for nested result error format\r\n      if (\r\n        \"result\" in data &&\r\n        data.result &&\r\n        typeof data.result === \"object\" &&\r\n        \"error\" in data.result &&\r\n        data.result.error !== 0\r\n      ) {\r\n        const errorMessage =\r\n          data.result.error_description ||\r\n          data.result.message ||\r\n          \"Unknown Zalo API error\";\r\n        throw new ZaloSDKError(errorMessage, data.result.error, data.result);\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Determine if request should be retried\r\n   */\r\n  private shouldRetry(error: AxiosError): boolean {\r\n    // Retry on network errors or 5xx server errors\r\n    return (\r\n      !error.response ||\r\n      (error.response.status >= 500 && error.response.status < 600)\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Create SDK error from axios error\r\n   */\r\n  private createSDKError(error: Error): ZaloSDKError {\r\n    if (error instanceof ZaloSDKError) {\r\n      return error;\r\n    }\r\n\r\n    if (error instanceof AxiosError) {\r\n      const response = (error as any).response;\r\n      if (response?.data) {\r\n        const errorCode = response.data.error || response.status;\r\n        const errorMessage =\r\n          response.data.error_description ||\r\n          response.data.message ||\r\n          error.message;\r\n        return new ZaloSDKError(errorMessage, errorCode, response.data);\r\n      }\r\n    }\r\n\r\n    return new ZaloSDKError(error.message, -1, error);\r\n  }\r\n\r\n  /**\r\n   * Delay execution for specified milliseconds\r\n   */\r\n  private delay(ms: number): Promise<void> {\r\n    return new Promise((resolve) => setTimeout(resolve, ms));\r\n  }\r\n}\r\n","/**\r\n * Main Zalo API client\r\n */\r\n\r\nimport { BaseClient } from \"./base-client\";\r\nimport { ZaloSDKConfig } from \"../types/common\";\r\n\r\n/**\r\n * Zalo API client for making HTTP requests\r\n */\r\nexport class ZaloClient extends BaseClient {\r\n  constructor(config: ZaloSDKConfig) {\r\n    super(config);\r\n  }\r\n\r\n  /**\r\n   * Make authenticated GET request to Zalo API\r\n   */\r\n  public async apiGet<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.get<T>(endpoint, accessToken, params);\r\n  }\r\n\r\n  /**\r\n   * Make GET request with custom headers\r\n   */\r\n  public async apiGetWithHeaders<T = any>(\r\n    endpoint: string,\r\n    headers: Record<string, string>\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method: \"GET\",\r\n      url: endpoint,\r\n      headers,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Make authenticated POST request to Zalo API\r\n   */\r\n  public async apiPost<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    data?: any,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.post<T>(endpoint, accessToken, data, params);\r\n  }\r\n\r\n  /**\r\n   * Make authenticated POST request to Zalo API with custom headers\r\n   */\r\n  public async apiPostWithHeaders<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    data?: any,\r\n    customHeaders?: Record<string, string>\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method: \"POST\",\r\n      url: endpoint,\r\n      headers: {\r\n        access_token: accessToken,\r\n        ...customHeaders,\r\n      },\r\n      data,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Make authenticated PUT request to Zalo API\r\n   */\r\n  public async apiPut<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    data?: any,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.put<T>(endpoint, accessToken, data, params);\r\n  }\r\n\r\n  /**\r\n   * Make authenticated DELETE request to Zalo API\r\n   */\r\n  public async apiDelete<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.delete<T>(endpoint, accessToken, params);\r\n  }\r\n\r\n  /**\r\n   * Upload file to Zalo API\r\n   */\r\n  public async apiUploadFile<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    file: Buffer | NodeJS.ReadableStream,\r\n    filename: string,\r\n    additionalFields?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.uploadFile<T>(\r\n      endpoint,\r\n      accessToken,\r\n      file,\r\n      filename,\r\n      additionalFields\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Make POST request with FormData (for file uploads)\r\n   */\r\n  public async apiPostFormData<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    formData: FormData\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method: \"POST\",\r\n      url: endpoint,\r\n      headers: {\r\n        access_token: accessToken,\r\n        // Don't set Content-Type header, let the browser set it with boundary\r\n      },\r\n      data: formData,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Make request to OAuth endpoints (without access token)\r\n   */\r\n  public async oauthRequest<T = any>(\r\n    method: \"GET\" | \"POST\",\r\n    endpoint: string,\r\n    data?: any,\r\n    headers?: Record<string, string>\r\n  ): Promise<T> {\r\n    const config = {\r\n      method,\r\n      url: endpoint,\r\n      headers,\r\n      data,\r\n    };\r\n\r\n    if (method === \"GET\") {\r\n      config.data = undefined;\r\n      return this.get<T>(endpoint, undefined, data);\r\n    } else {\r\n      return this.post<T>(endpoint, undefined, data);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Make request to ZNS API (different base URL)\r\n   */\r\n  public async znsRequest<T = any>(\r\n    method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\",\r\n    endpoint: string,\r\n    accessToken: string,\r\n    data?: any,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    const znsBaseUrl = \"https://business.openapi.zalo.me\";\r\n    const fullUrl = `${znsBaseUrl}${endpoint}`;\r\n\r\n    switch (method) {\r\n      case \"GET\":\r\n        return this.get<T>(fullUrl, accessToken, params);\r\n      case \"POST\":\r\n        return this.post<T>(fullUrl, accessToken, data, params);\r\n      case \"PUT\":\r\n        return this.put<T>(fullUrl, accessToken, data, params);\r\n      case \"DELETE\":\r\n        return this.delete<T>(fullUrl, accessToken, params);\r\n      default:\r\n        throw new Error(`Unsupported HTTP method: ${method}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Make request to OAuth endpoints with custom base URL\r\n   */\r\n  public async oauthRequestWithUrl<T = any>(\r\n    method: \"GET\" | \"POST\",\r\n    url: string,\r\n    data?: any,\r\n    headers?: Record<string, string>\r\n  ): Promise<T> {\r\n    return this.request<T>({\r\n      method,\r\n      url,\r\n      headers: headers || {},\r\n      data: method === \"POST\" ? data : undefined,\r\n      params: method === \"GET\" ? data : undefined,\r\n    });\r\n  }\r\n}\r\n","/**\r\n * Authentication service for Zalo API\r\n */\r\n\r\nimport { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  AccessToken,\r\n  RefreshTokenResponse,\r\n  AuthCodeParams,\r\n  RefreshTokenParams,\r\n  SocialUserInfo,\r\n  TokenValidation,\r\n  AuthScope,\r\n  PKCEConfig,\r\n  AuthUrls,\r\n  OAAuthResult,\r\n} from \"../types/auth\";\r\nimport { ZaloSDKError } from \"../types/common\";\r\nimport { createHash, randomBytes } from \"crypto\";\r\n\r\n/**\r\n * Authentication service for handling OAuth flows and token management\r\n */\r\nexport class AuthService {\r\n  // Zalo OAuth & Graph endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    auth: {\r\n      oaPermission: \"https://oauth.zaloapp.com/v4/oa/permission\",\r\n      socialPermission: \"https://oauth.zaloapp.com/v4/permission\",\r\n      oaToken: \"https://oauth.zaloapp.com/v4/oa/access_token\",\r\n      socialToken: \"https://oauth.zaloapp.com/v4/access_token\",\r\n      refreshOaToken: \"https://oauth.zaloapp.com/v4/oa/access_token\",\r\n      refreshSocialToken: \"https://openapi.zalo.me/v2.0/oauth/access_token\",\r\n    },\r\n    social: {\r\n      me: \"https://graph.zalo.me/v2.0/me\",\r\n    },\r\n  } as const;\r\n\r\n  constructor(\r\n    private readonly client: ZaloClient,\r\n    private readonly appId: string,\r\n    private readonly appSecret: string\r\n  ) {}\r\n\r\n  /**\r\n   * Generate PKCE code verifier and challenge for Social API\r\n   */\r\n  public generatePKCE(): PKCEConfig {\r\n    const codeVerifier = randomBytes(32).toString(\"base64url\");\r\n    const codeChallenge = createHash(\"sha256\")\r\n      .update(codeVerifier)\r\n      .digest(\"base64url\");\r\n\r\n    return {\r\n      code_verifier: codeVerifier,\r\n      code_challenge: codeChallenge,\r\n      code_challenge_method: \"S256\",\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Create OAuth authorization URL for Official Account with PKCE support\r\n   *\r\n   * @param redirectUri - The redirect URI after authorization\r\n   * @param state - Optional state parameter for security. If not provided, auto-generates with 'zalo_oa_' prefix\r\n   * @param usePkce - Whether to use PKCE for enhanced security. If true and pkce not provided, will auto-generate\r\n   * @param pkce - Optional PKCE configuration for enhanced security. If usePkce=true and this is not provided, will be auto-generated\r\n   * @returns Object containing the authorization URL, state, and PKCE config (if used)\r\n   */\r\n  public createOAAuthUrl(\r\n    redirectUri: string,\r\n    state?: string,\r\n    pkce?: PKCEConfig,\r\n    usePkce: boolean = false\r\n  ): OAAuthResult {\r\n    // Generate state with zalo_oa_ prefix if not provided\r\n    const finalState = state || `zalo_oa_${randomBytes(16).toString(\"hex\")}`;\r\n\r\n    // Auto-generate PKCE if usePkce is true but pkce is not provided\r\n    let finalPkce: PKCEConfig | undefined = pkce;\r\n    if (usePkce && !pkce) {\r\n      finalPkce = this.generatePKCE();\r\n    }\r\n\r\n    const params = new URLSearchParams({\r\n      app_id: this.appId,\r\n      redirect_uri: redirectUri,\r\n      state: finalState,\r\n    });\r\n\r\n    // Add PKCE parameters if PKCE is being used\r\n    if (usePkce && finalPkce) {\r\n      params.append(\"code_challenge\", finalPkce.code_challenge);\r\n      params.append(\"code_challenge_method\", finalPkce.code_challenge_method);\r\n    }\r\n\r\n    const url = `${this.endpoints.auth.oaPermission}?${params.toString()}`;\r\n\r\n    return {\r\n      url,\r\n      state: finalState,\r\n      pkce: usePkce ? finalPkce : undefined,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Create OAuth authorization URL for Social API\r\n   */\r\n  public createSocialAuthUrl(\r\n    redirectUri: string,\r\n    state?: string,\r\n    pkce?: PKCEConfig\r\n  ): string {\r\n    const params = new URLSearchParams({\r\n      app_id: this.appId,\r\n      redirect_uri: redirectUri,\r\n      state: state || \"social_auth\",\r\n    });\r\n\r\n    if (pkce) {\r\n      params.append(\"code_challenge\", pkce.code_challenge);\r\n      params.append(\"code_challenge_method\", pkce.code_challenge_method);\r\n    }\r\n\r\n    return `${this.endpoints.auth.socialPermission}?${params.toString()}`;\r\n  }\r\n\r\n  /**\r\n   * Exchange authorization code for Official Account access token\r\n   * Now supports PKCE code_verifier for enhanced security\r\n   */\r\n  public async getOAAccessToken(params: AuthCodeParams): Promise<AccessToken> {\r\n    try {\r\n      const url = this.endpoints.auth.oaToken;\r\n\r\n      // Prepare form data according to API docs\r\n      const formData = new URLSearchParams();\r\n      formData.append(\"code\", params.code);\r\n      formData.append(\"app_id\", params.app_id);\r\n      formData.append(\"grant_type\", \"authorization_code\");\r\n\r\n      // Add code_verifier if provided (for PKCE)\r\n      if (params.code_verifier) {\r\n        formData.append(\"code_verifier\", params.code_verifier);\r\n      }\r\n\r\n      let result: any;\r\n      try {\r\n        result = await this.client.oauthRequestWithUrl(\r\n          \"POST\",\r\n          url,\r\n          formData.toString(),\r\n          {\r\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\r\n            \"secret_key\": params.app_secret,\r\n          }\r\n        );\r\n      } catch (error) {\r\n        // If the error contains access_token, it means the request was successful\r\n        // but Zalo API returned a warning/error code along with valid tokens\r\n        if (error instanceof ZaloSDKError && error.details?.access_token) {\r\n          result = error.details;\r\n        } else {\r\n          throw error;\r\n        }\r\n      }\r\n\r\n      // Check if we have access_token (prioritize token presence over error codes)\r\n      if (result.access_token) {\r\n        return {\r\n          access_token: result.access_token,\r\n          expires_in: parseInt(result.expires_in) || result.expires_in,\r\n          refresh_token: result.refresh_token,\r\n        };\r\n      }\r\n\r\n      // If no access_token but we have data wrapper, check inside\r\n      if (result.data?.access_token) {\r\n        return {\r\n          access_token: result.data.access_token,\r\n          expires_in: parseInt(result.data.expires_in) || result.data.expires_in,\r\n          refresh_token: result.data.refresh_token,\r\n        };\r\n      }\r\n\r\n      // Only check error codes if we don't have access_token\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.error_description ||\r\n            result.message ||\r\n            \"Failed to get OA access token\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      throw new ZaloSDKError(\"No access token received from Zalo API\", -1, result);\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get OA access token: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Exchange authorization code for Social API access token\r\n   */\r\n  public async getSocialAccessToken(\r\n    params: AuthCodeParams\r\n  ): Promise<AccessToken> {\r\n    try {\r\n      const url = this.endpoints.auth.socialToken;\r\n\r\n      const formData = new URLSearchParams();\r\n      formData.append(\"code\", params.code);\r\n      formData.append(\"app_id\", params.app_id);\r\n      formData.append(\"grant_type\", \"authorization_code\");\r\n\r\n      if (params.code_verifier) {\r\n        formData.append(\"code_verifier\", params.code_verifier);\r\n      }\r\n\r\n      let result: any;\r\n      try {\r\n        result = await this.client.oauthRequestWithUrl(\r\n          \"POST\",\r\n          url,\r\n          formData.toString(),\r\n          {\r\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\r\n            secret_key: params.app_secret,\r\n          }\r\n        );\r\n      } catch (error) {\r\n        // If the error contains access_token, it means the request was successful\r\n        // but Zalo API returned a warning/error code along with valid tokens\r\n        if (error instanceof ZaloSDKError && error.details?.access_token) {\r\n          result = error.details;\r\n        } else {\r\n          throw error;\r\n        }\r\n      }\r\n\r\n      // Check if we have access_token (prioritize token presence over error codes)\r\n      if (result.access_token) {\r\n        return {\r\n          access_token: result.access_token,\r\n          expires_in: result.expires_in,\r\n          refresh_token: result.refresh_token,\r\n        };\r\n      }\r\n\r\n      // If no access_token but we have data wrapper, check inside\r\n      if (result.data?.access_token) {\r\n        return {\r\n          access_token: result.data.access_token,\r\n          expires_in: result.data.expires_in,\r\n          refresh_token: result.data.refresh_token,\r\n        };\r\n      }\r\n\r\n      // Only check error codes if we don't have access_token\r\n      if (result.error && result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.error_description ||\r\n            result.message ||\r\n            \"Failed to get Social access token\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      throw new ZaloSDKError(\"No access token received from Zalo API\", -1, result);\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get Social access token: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Refresh Official Account access token\r\n   */\r\n  public async refreshOAAccessToken(\r\n    params: RefreshTokenParams\r\n  ): Promise<AccessToken> {\r\n    try {\r\n      const url = this.endpoints.auth.refreshOaToken;\r\n\r\n      const formData = new URLSearchParams();\r\n      formData.append(\"refresh_token\", params.refresh_token);\r\n      formData.append(\"app_id\", params.app_id);\r\n      formData.append(\"grant_type\", \"refresh_token\");\r\n\r\n      const result = await this.client.oauthRequestWithUrl(\r\n        \"POST\",\r\n        url,\r\n        formData.toString(),\r\n        {\r\n          \"Content-Type\": \"application/x-www-form-urlencoded\",\r\n          secret_key: params.app_secret,\r\n        }\r\n      );\r\n\r\n      if (!result.access_token) {\r\n        throw new ZaloSDKError(\r\n          \"No access token received when refreshing OA token\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      return {\r\n        access_token: result.access_token,\r\n        refresh_token: result.refresh_token,\r\n        expires_in: parseInt(result.expires_in),\r\n      };\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to refresh OA access token: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Refresh Social API access token\r\n   */\r\n  public async refreshSocialAccessToken(\r\n    params: RefreshTokenParams\r\n  ): Promise<RefreshTokenResponse> {\r\n    try {\r\n      const endpoint = this.endpoints.auth.refreshSocialToken;\r\n      const requestParams = {\r\n        app_id: params.app_id,\r\n        grant_type: \"refresh_token\",\r\n        refresh_token: params.refresh_token,\r\n      };\r\n\r\n      const result = await this.client.apiGet(endpoint, \"\", requestParams);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.error_description ||\r\n            result.message ||\r\n            \"Failed to refresh Social access token\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\r\n          \"No data received when refreshing Social token\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to refresh Social access token: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get Social user information\r\n   */\r\n  public async getSocialUserInfo(\r\n    accessToken: string,\r\n    fields: string = \"id,name,picture\"\r\n  ): Promise<SocialUserInfo> {\r\n    try {\r\n      const url = this.endpoints.social.me;\r\n      const params = { fields };\r\n\r\n      const result = await this.client.oauthRequestWithUrl(\"GET\", url, params, {\r\n        access_token: accessToken,\r\n      });\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get Social user info\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.id) {\r\n        throw new ZaloSDKError(\"No user data received from Social API\", -1);\r\n      }\r\n\r\n      return {\r\n        id: result.id,\r\n        name: result.name,\r\n        picture: result.picture,\r\n        is_sensitive: result.is_sensitive,\r\n      };\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get Social user info: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate access token by attempting to get user info\r\n   */\r\n  public async validateAccessToken(\r\n    accessToken: string,\r\n    scope: AuthScope = AuthScope.SOCIAL\r\n  ): Promise<TokenValidation> {\r\n    try {\r\n      if (scope === AuthScope.SOCIAL) {\r\n        const userInfo = await this.getSocialUserInfo(accessToken, \"id\");\r\n        return {\r\n          valid: true,\r\n          user_info: userInfo,\r\n        };\r\n      } else {\r\n        // For OA tokens, we could try to get OA info\r\n        // This is a simplified validation\r\n        return { valid: true };\r\n      }\r\n    } catch (error) {\r\n      return { valid: false };\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get all authentication URLs with optional PKCE support\r\n   */\r\n  public getAuthUrls(\r\n    redirectUri: string,\r\n    usePkce: boolean = false,\r\n    pkce?: PKCEConfig\r\n  ): AuthUrls {\r\n    const oaAuthResult = this.createOAAuthUrl(\r\n      redirectUri,\r\n      undefined,\r\n      pkce,\r\n      usePkce\r\n    );\r\n\r\n    return {\r\n      oa_auth_url: oaAuthResult.url,\r\n      social_auth_url: this.createSocialAuthUrl(redirectUri, undefined, pkce),\r\n      token_url: this.endpoints.auth.socialToken,\r\n      refresh_url: this.endpoints.auth.refreshOaToken,\r\n    };\r\n  }\r\n}\r\n","/**\r\n * Official Account (OA) service for Zalo API\r\n */\r\n\r\nimport { ZaloClient } from '../clients/zalo-client';\r\nimport {\r\n  OAInfo,\r\n  MessageQuota,\r\n  QuotaMessageRequest,\r\n  QuotaMessageResponse,\r\n  QuotaAsset,\r\n  OASettings,\r\n  OAStatistics,\r\n  UpdateOAProfileRequest,\r\n  OAVerificationRequest,\r\n  GMFProductType,\r\n  QuotaType,\r\n} from '../types/oa';\r\nimport { ZaloResponse, ZaloSDKError } from '../types/common';\r\n\r\n/**\r\n * Official Account service for managing OA information and settings\r\n */\r\nexport class OAService {\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    // OA information\r\n    info: \"https://openapi.zalo.me/v2.0/oa/getoa\",\r\n    // Quota endpoints\r\n    quota: {\r\n      message: \"https://openapi.zalo.me/v3.0/oa/quota/message\",\r\n    },\r\n    // Profile management\r\n    profile: {\r\n      update: \"https://openapi.zalo.me/v2.0/oa/update\",\r\n    },\r\n    // Statistics\r\n    statistics: {\r\n      overview: \"https://openapi.zalo.me/v2.0/oa/statistics\",\r\n    },\r\n  };\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Get Official Account information\r\n   */\r\n  public async getOAInfo(accessToken: string): Promise<OAInfo> {\r\n    try {\r\n      const endpoint = this.endpoints.info;\r\n      const result: ZaloResponse<OAInfo> = await this.client.apiGet(endpoint, accessToken);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || 'Failed to get OA information',\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError('No OA data received', -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(`Failed to get OA info: ${error.message}`, -1, error);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get detailed quota message information\r\n   */\r\n  public async getDetailedQuotaMessage(\r\n    accessToken: string,\r\n    request: QuotaMessageRequest\r\n  ): Promise<QuotaAsset[]> {\r\n    try {\r\n      const endpoint = this.endpoints.quota.message;\r\n      const result: QuotaMessageResponse = await this.client.apiPost(endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || 'Failed to get detailed quota message',\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return result.data || [];\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(`Failed to get detailed quota message: ${error.message}`, -1, error);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get quota for specific product type\r\n   */\r\n  public async getQuotaByProductType(\r\n    accessToken: string,\r\n    productType: 'cs' | 'transaction' | 'gmf10' | 'gmf50' | 'gmf100',\r\n    quotaType?: 'sub_quota' | 'purchase_quota' | 'reward_quota'\r\n  ): Promise<QuotaAsset[]> {\r\n    const request: QuotaMessageRequest = {\r\n      quota_owner: 'OA',\r\n      product_type: productType,\r\n      quota_type: quotaType,\r\n    };\r\n\r\n    return this.getDetailedQuotaMessage(accessToken, request);\r\n  }\r\n\r\n  /**\r\n   * Get available GMF quota\r\n   */\r\n  public async getGMFQuota(\r\n    accessToken: string,\r\n    gmfType: GMFProductType = GMFProductType.GMF10\r\n  ): Promise<QuotaAsset[]> {\r\n    return this.getQuotaByProductType(accessToken, gmfType);\r\n  }\r\n\r\n  /**\r\n   * Get consultation message quota\r\n   */\r\n  public async getConsultationQuota(accessToken: string): Promise<QuotaAsset[]> {\r\n    return this.getQuotaByProductType(accessToken, 'cs');\r\n  }\r\n\r\n  /**\r\n   * Get transaction message quota\r\n   */\r\n  public async getTransactionQuota(accessToken: string): Promise<QuotaAsset[]> {\r\n    return this.getQuotaByProductType(accessToken, 'transaction');\r\n  }\r\n\r\n  /**\r\n   * Update OA profile information\r\n   */\r\n  public async updateOAProfile(\r\n    accessToken: string,\r\n    profileData: UpdateOAProfileRequest\r\n  ): Promise<boolean> {\r\n    try {\r\n      // Note: This endpoint might not be available in all Zalo API versions\r\n      // This is a placeholder implementation\r\n      const endpoint = this.endpoints.profile.update;\r\n      const result = await this.client.apiPost(endpoint, accessToken, profileData);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || 'Failed to update OA profile',\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return true;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(`Failed to update OA profile: ${error.message}`, -1, error);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get OA statistics (if available)\r\n   */\r\n  public async getOAStatistics(accessToken: string): Promise<OAStatistics> {\r\n    try {\r\n      // Note: This endpoint might not be available in all Zalo API versions\r\n      // This is a placeholder implementation\r\n      const endpoint = this.endpoints.statistics.overview;\r\n      const result = await this.client.apiGet(endpoint, accessToken);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || 'Failed to get OA statistics',\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return result.data || {\r\n        total_followers: 0,\r\n        new_followers_today: 0,\r\n        messages_sent_today: 0,\r\n        messages_received_today: 0,\r\n      };\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(`Failed to get OA statistics: ${error.message}`, -1, error);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Lấy thông tin quota tổng quan cho tin nhắn (tổng số asset và số còn khả dụng)\r\n   * Lưu ý: Zalo hiện cung cấp API chi tiết qua endpoint quota/message theo asset.\r\n   * Hàm này tổng hợp đơn giản để trả về cấu trúc MessageQuota phục vụ SDK.\r\n   */\r\n  public async getMessageQuota(accessToken: string): Promise<MessageQuota> {\r\n    try {\r\n      // Gọi endpoint quota/message không chỉ định product_type để lấy toàn bộ asset\r\n      const request: QuotaMessageRequest = {\r\n        quota_owner: 'OA',\r\n      };\r\n      const assets = await this.getDetailedQuotaMessage(accessToken, request);\r\n\r\n      // Đếm tổng asset và số asset còn khả dụng\r\n      const totalAssets = assets.length;\r\n      const availableAssets = assets.filter((a) => a.status === 'available')\r\n        .length;\r\n\r\n      // Trả về dạng MessageQuota tối giản\r\n      const quota: MessageQuota = {\r\n        // Sử dụng tổng số asset làm daily_quota (ước lượng theo asset)\r\n        daily_quota: totalAssets,\r\n        // Số còn lại là số asset đang ở trạng thái available\r\n        remaining_quota: availableAssets,\r\n        // Quota type tổng hợp vì có thể gồm nhiều loại\r\n        quota_type: 'mixed',\r\n        // Không có reset_time từ API asset -> trả 0\r\n        reset_time: 0,\r\n      };\r\n\r\n      return quota;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get message quota: ${((error as unknown) as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Check if OA has sufficient quota for message type\r\n   */\r\n  public async hasQuotaForMessageType(\r\n    accessToken: string,\r\n    messageType: 'cs' | 'transaction' | 'promotion',\r\n    requiredCount: number = 1\r\n  ): Promise<boolean> {\r\n    try {\r\n      let productType: 'cs' | 'transaction';\r\n      \r\n      if (messageType === 'promotion') {\r\n        // Promotion messages might use different quota logic\r\n        return true; // Placeholder\r\n      } else {\r\n        productType = messageType;\r\n      }\r\n\r\n      const quotaAssets = await this.getQuotaByProductType(accessToken, productType);\r\n      \r\n      // Check if there are available assets\r\n      const availableAssets = quotaAssets.filter(asset => asset.status === 'available');\r\n      \r\n      if (availableAssets.length === 0) {\r\n        return false;\r\n      }\r\n\r\n      // For simplicity, assume each available asset can handle the required count\r\n      return true;\r\n    } catch (error) {\r\n      // If we can't check quota, assume it's available to avoid blocking\r\n      return true;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get quota summary for all message types\r\n   */\r\n  public async getQuotaSummary(accessToken: string): Promise<{\r\n    consultation: QuotaAsset[];\r\n    transaction: QuotaAsset[];\r\n    gmf10: QuotaAsset[];\r\n    gmf50: QuotaAsset[];\r\n    gmf100: QuotaAsset[];\r\n  }> {\r\n    try {\r\n      const [consultation, transaction, gmf10, gmf50, gmf100] = await Promise.all([\r\n        this.getConsultationQuota(accessToken),\r\n        this.getTransactionQuota(accessToken),\r\n        this.getGMFQuota(accessToken, GMFProductType.GMF10),\r\n        this.getGMFQuota(accessToken, GMFProductType.GMF50),\r\n        this.getGMFQuota(accessToken, GMFProductType.GMF100),\r\n      ]);\r\n\r\n      return {\r\n        consultation,\r\n        transaction,\r\n        gmf10,\r\n        gmf50,\r\n        gmf100,\r\n      };\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(`Failed to get quota summary: ${error.message}`, -1, error);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate OA access token by getting OA info\r\n   */\r\n  public async validateOAToken(accessToken: string): Promise<boolean> {\r\n    try {\r\n      await this.getOAInfo(accessToken);\r\n      return true;\r\n    } catch (error) {\r\n      return false;\r\n    }\r\n  }\r\n}\r\n","/**\r\n * User management service for Zalo API\r\n * Handles all user-related operations with specific endpoints\r\n */\r\n\r\nimport { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  UserInfo,\r\n  UserListRequest,\r\n  UserListResponse,\r\n  UserLabel,\r\n  TagUserRequest,\r\n  RemoveTagFromUserRequest,\r\n  GetTagsResponse,\r\n  DeleteTagRequest,\r\n  UpdateUserRequest,\r\n  DeleteUserInfoRequest,\r\n  GetUserCustomInfoRequest,\r\n  UserCustomInfoResponse,\r\n  UpdateUserCustomInfoRequest,\r\n  AdvancedUserFilters,\r\n} from \"../types/user\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\n\r\n/**\r\n * User management service for handling user operations\r\n * Each method uses specific Zalo API endpoints\r\n */\r\nexport class UserService {\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    // User management endpoints\r\n    user: {\r\n      getList: \"https://openapi.zalo.me/v3.0/oa/user/getlist\",\r\n      postList: \"https://openapi.zalo.me/v3.0/oa/user/getlist\",\r\n      getDetail: \"https://openapi.zalo.me/v3.0/oa/user/detail\",\r\n      update: \"https://openapi.zalo.me/v3.0/oa/user/update\",\r\n      delete: \"https://openapi.zalo.me/v2.0/oa/deletefollowerinfo\",\r\n      getCustomInfo: \"https://openapi.zalo.me/v3.0/oa/user/detail/custominfo\",\r\n      updateCustomInfo:\r\n        \"https://openapi.zalo.me/v3.0/oa/user/update/custominfo\",\r\n    },\r\n    // Tag management endpoints\r\n    tag: {\r\n      addToUser: \"https://openapi.zalo.me/v2.0/oa/tag/tagfollower\",\r\n      removeFromUser: \"https://openapi.zalo.me/v2.0/oa/tag/rmfollowerfromtag\",\r\n      getList: \"https://openapi.zalo.me/v2.0/oa/tag/gettagsofoa\",\r\n      delete: \"https://openapi.zalo.me/v2.0/oa/tag/rmtag\",\r\n    },\r\n  };\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Truy xuất chi tiết người dùng\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/user/detail\r\n   * Method: GET with data parameter as JSON string\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param userId User ID to get details for\r\n   * @returns Promise<UserInfo> - Detailed user information\r\n   */\r\n  public async getUserInfo(\r\n    accessToken: string,\r\n    userId: string\r\n  ): Promise<UserInfo> {\r\n    try {\r\n      // Build data object according to API spec\r\n      const dataObject = { user_id: userId };\r\n\r\n      // Send data as JSON string parameter according to API spec\r\n      const params = {\r\n        data: JSON.stringify(dataObject),\r\n      };\r\n\r\n      const result: ZaloResponse<UserInfo> = await this.client.apiGet(\r\n        this.endpoints.user.getDetail,\r\n        accessToken,\r\n        params\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get user information\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No user data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      this.handleError(error, \"Failed to get user info\");\r\n    }\r\n  }\r\n\r\n  /**\r\n * Truy xuất chi tiết người dùng (bản dùng POST)\r\n * Endpoint: https://openapi.zalo.me/v3.0/oa/user/detail\r\n * Method: POST với body JSON\r\n *\r\n * @param accessToken OA access token\r\n * @param userId ID người dùng cần lấy thông tin\r\n * @returns Promise<UserInfo> - Thông tin chi tiết người dùng\r\n */\r\npublic async postUserInfo(\r\n  accessToken: string,\r\n  userId: string\r\n): Promise<UserInfo> {\r\n  try {\r\n    // Tạo body JSON theo yêu cầu API\r\n    const body = {\r\n      user_id: userId,\r\n    };\r\n\r\n    // Gửi POST request thay vì GET\r\n    const result: ZaloResponse<UserInfo> = await this.client.apiPost(\r\n      this.endpoints.user.getDetail,\r\n      accessToken,\r\n      body\r\n    );\r\n\r\n    // Kiểm tra lỗi từ API Zalo\r\n    if (result.error !== 0) {\r\n      throw new ZaloSDKError(\r\n        result.message || \"Failed to get user information (POST)\",\r\n        result.error,\r\n        result\r\n      );\r\n    }\r\n\r\n    // Kiểm tra dữ liệu trả về\r\n    if (!result.data) {\r\n      throw new ZaloSDKError(\"No user data received from POST request\", -1);\r\n    }\r\n\r\n    return result.data;\r\n  } catch (error) {\r\n    if (error instanceof ZaloSDKError) {\r\n      throw error;\r\n    }\r\n    this.handleError(error, \"Failed to get user info (POST)\");\r\n  }\r\n}\r\n\r\n\r\n  /**\r\n   * Truy xuất danh sách người dùng\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/user/getlist\r\n   * Method: GET with data parameter as JSON string\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param request User list request parameters\r\n   * @returns Promise<UserListResponse> - List of users with pagination info\r\n   */\r\n  public async getUserList(\r\n    accessToken: string,\r\n    request: UserListRequest\r\n  ): Promise<UserListResponse> {\r\n    try {\r\n      // Build data object according to API spec\r\n      const dataObject = {\r\n        offset: request.offset,\r\n        count: Math.min(request.count, 50), // Max 50 users per request\r\n        ...(request.tag_name && { tag_name: request.tag_name }),\r\n        ...(request.last_interaction_period && {\r\n          last_interaction_period: request.last_interaction_period,\r\n        }),\r\n        ...(request.is_follower !== undefined && {\r\n          is_follower: request.is_follower,\r\n        }),\r\n      };\r\n\r\n      // Send data as JSON string parameter according to API spec\r\n      const params = {\r\n        data: JSON.stringify(dataObject),\r\n      };\r\n\r\n      const result: ZaloResponse<UserListResponse> = await this.client.apiGet(\r\n        this.endpoints.user.getList,\r\n        accessToken,\r\n        params\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get user list\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No user list data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      this.handleError(error, \"Failed to get user list\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Truy xuất danh sách người dùng (POST method)\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/user/getlist\r\n   * Method: POST with data in request body\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param request User list request parameters\r\n   * @returns Promise<UserListResponse> - List of users with pagination info\r\n   */\r\n  public async postUserList(\r\n    accessToken: string,\r\n    request: UserListRequest\r\n  ): Promise<UserListResponse> {\r\n    try {\r\n      // Build data object according to API spec\r\n      const dataObject = {\r\n        offset: request.offset,\r\n        count: Math.min(request.count, 50), // Max 50 users per request\r\n        ...(request.tag_name && { tag_name: request.tag_name }),\r\n        ...(request.last_interaction_period && {\r\n          last_interaction_period: request.last_interaction_period,\r\n        }),\r\n        ...(request.is_follower !== undefined && {\r\n          is_follower: request.is_follower,\r\n        }),\r\n      };\r\n\r\n      const result: ZaloResponse<UserListResponse> = await this.client.apiPost(\r\n        this.endpoints.user.postList,\r\n        accessToken,\r\n        dataObject\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get user list\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No user list data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      this.handleError(error, \"Failed to get user list\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get all users with automatic pagination\r\n   * Lấy tất cả users với phân trang tự động, đảm bảo lấy 100%\r\n   *\r\n   * @param accessToken Access token của OA\r\n   * @param filters Bộ lọc tùy chọn\r\n   * @returns Promise<UserInfo[]> - Danh sách đầy đủ tất cả users\r\n   */\r\n  public async getAllUsers(\r\n    accessToken: string,\r\n    filters?: {\r\n      tag_name?: string;\r\n      last_interaction_period?: string;\r\n      is_follower?: boolean;\r\n    }\r\n  ): Promise<UserInfo[]> {\r\n    console.log('[getAllUsers] Bắt đầu lấy danh sách users với filters:', filters);\r\n    const users: UserInfo[] = [];\r\n    let offset = 0;\r\n    const count = 50; // Max per request\r\n    let totalUsers = 0;\r\n    let fetchedUsers = 0;\r\n\r\n    let failedUsers = 0;\r\n\r\n    try {\r\n      while (true) {\r\n        console.log(`[getAllUsers] Đang lấy batch tại offset=${offset}, count=${count}`);\r\n        const userListResponse = await this.postUserList(accessToken, {\r\n          offset,\r\n          count,\r\n          ...filters,\r\n        });\r\n        console.log(`[getAllUsers] Nhận được ${userListResponse.users.length} user IDs từ postUserList`);\r\n\r\n        // Lần đầu tiên, lưu tổng số users\r\n        if (offset === 0) {\r\n          totalUsers = userListResponse.total;\r\n          console.log(`[getAllUsers] ✓ Total users to fetch: ${totalUsers}`);\r\n\r\n          // Cảnh báo nếu vượt quá giới hạn Zalo API\r\n          if (totalUsers > 10000) {\r\n            console.warn(\r\n              `[getAllUsers] ⚠ Warning: Total users (${totalUsers}) exceeds Zalo API limit (10000). ` +\r\n                `Only first 10000 users can be fetched due to max offset = 9951.`\r\n            );\r\n          }\r\n        }\r\n\r\n        // Get detailed info for each user (sequential to avoid rate limit)\r\n        // Xử lý tuần tự để tránh bị Zalo API rate limit\r\n        const userIds = userListResponse.users.map((u) => u.user_id);\r\n        console.log(`[getAllUsers] Bắt đầu lấy chi tiết cho ${userIds.length} users...`);\r\n        const userInfos: UserInfo[] = [];\r\n        \r\n        for (let i = 0; i < userIds.length; i++) {\r\n          const userId = userIds[i];\r\n          console.log(`[getAllUsers] ├─ [${i + 1}/${userIds.length}] Đang lấy info cho user: ${userId}`);\r\n          \r\n          try {\r\n            const userInfo = await this.postUserInfo(accessToken, userId);\r\n            userInfos.push(userInfo);\r\n            console.log(`[getAllUsers] │  ✓ Thành công: ${userInfo.display_name || 'N/A'}`);\r\n          } catch (error) {\r\n            failedUsers++;\r\n            console.warn(`[getAllUsers] │  ⚠ Skip user ${userId} do lỗi: ${error.message}`);\r\n            // Tiếp tục với user tiếp theo thay vì throw error\r\n          }\r\n          \r\n          // Small delay between requests to avoid rate limiting\r\n          if (i < userIds.length - 1) {\r\n            await new Promise((resolve) => setTimeout(resolve, 100));\r\n          }\r\n        }\r\n        console.log(`[getAllUsers] ✓ Hoàn thành lấy chi tiết ${userInfos.length} users trong batch này`);\r\n\r\n        users.push(...userInfos);\r\n        fetchedUsers += userInfos.length;\r\n\r\n        console.log(`[getAllUsers] 📊 Progress: Đã lấy ${fetchedUsers}/${totalUsers} users (${Math.round((fetchedUsers/totalUsers)*100)}%)`);\r\n\r\n        // Điều kiện dừng ĐÚNG:\r\n        // 1. Không còn users trong response\r\n        // 2. Hoặc đã lấy đủ số lượng theo total\r\n        // 3. Hoặc số users trả về ít hơn count (page cuối)\r\n        if (\r\n          userListResponse.users.length === 0 ||\r\n          fetchedUsers >= totalUsers ||\r\n          userListResponse.users.length < count\r\n        ) {\r\n          console.log(`[getAllUsers] 🛑 Điều kiện dừng:`, {\r\n            noMoreUsers: userListResponse.users.length === 0,\r\n            reachedTotal: fetchedUsers >= totalUsers,\r\n            lastPage: userListResponse.users.length < count\r\n          });\r\n          break;\r\n        }\r\n\r\n        offset += count;\r\n        console.log(`[getAllUsers] ➡ Chuyển sang batch tiếp theo, offset=${offset}`);\r\n\r\n        // Protection: Tránh infinite loop\r\n        // Zalo giới hạn max offset = 9951 (tương ứng 10000 người dùng)\r\n        if (offset > 9951) {\r\n          console.warn(\r\n            `[getAllUsers] ⚠ Reached Zalo maximum offset limit (9951), stopping pagination`\r\n          );\r\n          break;\r\n        }\r\n      }\r\n\r\n      console.log(`[getAllUsers] ✅ HOÀN THÀNH: Successfully fetched ${users.length}/${totalUsers} users` + (failedUsers > 0 ? ` (${failedUsers} failed/skipped)` : ''));\r\n      return users;\r\n    } catch (error) {\r\n      console.error('[getAllUsers] ❌ LỖI:', {\r\n        message: error.message,\r\n        code: error.code || error.error,\r\n        offset: offset,\r\n        fetchedSoFar: fetchedUsers,\r\n        totalTarget: totalUsers\r\n      });\r\n      \r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      this.handleError(error, \"Failed to get all users\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get all user IDs only (without detailed info) - Faster version\r\n   * Chỉ lấy danh sách user ID, không lấy thông tin chi tiết - Nhanh hơn\r\n   *\r\n   * @param accessToken Access token của OA\r\n   * @param filters Bộ lọc tùy chọn\r\n   * @returns Promise<string[]> - Danh sách user IDs\r\n   */\r\n  public async getAllUserIds(\r\n    accessToken: string,\r\n    filters?: {\r\n      tag_name?: string;\r\n      last_interaction_period?: string;\r\n      is_follower?: boolean;\r\n    }\r\n  ): Promise<string[]> {\r\n    const userIds: string[] = [];\r\n    let offset = 0;\r\n    const count = 50; // Max per request\r\n    let totalUsers = 0;\r\n\r\n    try {\r\n      while (true) {\r\n        const userListResponse = await this.postUserList(accessToken, {\r\n          offset,\r\n          count,\r\n          ...filters,\r\n        });\r\n\r\n        // Lần đầu tiên, lưu tổng số users\r\n        if (offset === 0) {\r\n          totalUsers = userListResponse.total;\r\n          console.log(`Total user IDs to fetch: ${totalUsers}`);\r\n\r\n          // Cảnh báo nếu vượt quá giới hạn Zalo API\r\n          if (totalUsers > 10000) {\r\n            console.warn(\r\n              `Warning: Total users (${totalUsers}) exceeds Zalo API limit (10000). ` +\r\n                `Only first 10000 users can be fetched due to max offset = 9951.`\r\n            );\r\n          }\r\n        }\r\n\r\n        // Chỉ lấy user IDs, không lấy detailed info\r\n        const currentUserIds = userListResponse.users.map((u) => u.user_id);\r\n        userIds.push(...currentUserIds);\r\n\r\n        console.log(\r\n          `Fetched ${userIds.length}/${Math.min(totalUsers, 10000)} user IDs`\r\n        );\r\n\r\n        // Điều kiện dừng\r\n        if (\r\n          userListResponse.users.length === 0 ||\r\n          userIds.length >= Math.min(totalUsers, 10000) ||\r\n          userListResponse.users.length < count\r\n        ) {\r\n          break;\r\n        }\r\n\r\n        offset += count;\r\n\r\n        // Protection: Tránh infinite loop\r\n        if (offset > 9951) {\r\n          console.warn(\r\n            \"Reached Zalo maximum offset limit (9951), stopping pagination\"\r\n          );\r\n          break;\r\n        }\r\n      }\r\n\r\n      console.log(`Successfully fetched ${userIds.length} user IDs`);\r\n      return userIds;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      this.handleError(error, \"Failed to get all user IDs\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get all users with advanced filtering based on UserInfo fields\r\n   * Lấy tất cả users với bộ lọc nâng cao dựa trên các trường của UserInfo\r\n   *\r\n   * @param accessToken Access token của OA\r\n   * @param filters Bộ lọc nâng cao\r\n   * @returns Promise<UserInfo[]> - Danh sách users đã được lọc\r\n   */\r\n  public async getAllUsersWithAdvancedFilters(\r\n    accessToken: string,\r\n    filters: AdvancedUserFilters = {}\r\n  ): Promise<UserInfo[]> {\r\n    try {\r\n      // Optimization: If only userIds filter is provided, get users directly\r\n      const hasOnlyUserIdsFilter =\r\n        filters.userIds &&\r\n        filters.userIds.length > 0 &&\r\n        Object.keys(filters).length === 1;\r\n\r\n      if (hasOnlyUserIdsFilter) {\r\n        console.log(`Fetching ${filters.userIds!.length} specific users by IDs`);\r\n        const userInfoPromises = filters.userIds!.map((userId) =>\r\n          this.getUserInfo(accessToken, userId)\r\n        );\r\n        return await Promise.all(userInfoPromises);\r\n      }\r\n\r\n      // First get all users using basic Zalo API filters\r\n      const basicFilters = {\r\n        ...(filters.tag_name && { tag_name: filters.tag_name }),\r\n        ...(filters.last_interaction_period && {\r\n          last_interaction_period: filters.last_interaction_period,\r\n        }),\r\n        ...(filters.is_follower !== undefined && {\r\n          is_follower: filters.is_follower,\r\n        }),\r\n      };\r\n\r\n      console.log(\"Fetching users with basic filters:\", basicFilters);\r\n      const allUsers = await this.getAllUsers(accessToken, basicFilters);\r\n\r\n      // Apply advanced client-side filters\r\n      const filteredUsers = allUsers.filter((user) => {\r\n        // Filter by specific user IDs (exact match) - early return for performance\r\n        if (filters.userIds && filters.userIds.length > 0) {\r\n          if (!filters.userIds.includes(user.user_id)) {\r\n            return false;\r\n          }\r\n        }\r\n\r\n        // Filter by display name (contains)\r\n        if (\r\n          filters.display_name_contains &&\r\n          !user.display_name\r\n            ?.toLowerCase()\r\n            .includes(filters.display_name_contains.toLowerCase())\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        // Filter by user alias (contains)\r\n        if (\r\n          filters.user_alias_contains &&\r\n          !user.user_alias\r\n            ?.toLowerCase()\r\n            .includes(filters.user_alias_contains.toLowerCase())\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        // Filter by sensitive status\r\n        if (\r\n          filters.is_sensitive !== undefined &&\r\n          user.is_sensitive !== filters.is_sensitive\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        // Filter by interaction date range\r\n        if (filters.last_interaction_date_range) {\r\n          const userDate = this.parseDate(user.user_last_interaction_date);\r\n          const fromDate = this.parseDate(\r\n            filters.last_interaction_date_range.from\r\n          );\r\n          const toDate = this.parseDate(filters.last_interaction_date_range.to);\r\n\r\n          if (userDate < fromDate || userDate > toDate) {\r\n            return false;\r\n          }\r\n        }\r\n\r\n        // Filter by multiple tags (OR operation)\r\n        if (filters.tag_names && filters.tag_names.length > 0) {\r\n          const userTags = user.tags_and_notes_info.tag_names || [];\r\n          const hasAnyTag = filters.tag_names.some((tag) =>\r\n            userTags.includes(tag)\r\n          );\r\n          if (!hasAnyTag) {\r\n            return false;\r\n          }\r\n        }\r\n\r\n        // Filter by required all tags (AND operation)\r\n        if (filters.require_all_tags && filters.require_all_tags.length > 0) {\r\n          const userTags = user.tags_and_notes_info.tag_names || [];\r\n          const hasAllTags = filters.require_all_tags.every((tag) =>\r\n            userTags.includes(tag)\r\n          );\r\n          if (!hasAllTags) {\r\n            return false;\r\n          }\r\n        }\r\n\r\n        // Exclude users with specific tags\r\n        if (filters.exclude_tags && filters.exclude_tags.length > 0) {\r\n          const userTags = user.tags_and_notes_info.tag_names || [];\r\n          const hasExcludedTag = filters.exclude_tags.some((tag) =>\r\n            userTags.includes(tag)\r\n          );\r\n          if (hasExcludedTag) {\r\n            return false;\r\n          }\r\n        }\r\n\r\n        // Filter by notes content (contains)\r\n        if (filters.notes_contains) {\r\n          const userNotes = user.tags_and_notes_info.notes || [];\r\n          const hasNote = userNotes.some((note) =>\r\n            note.toLowerCase().includes(filters.notes_contains!.toLowerCase())\r\n          );\r\n          if (!hasNote) {\r\n            return false;\r\n          }\r\n        }\r\n\r\n        // Shared info filters\r\n        if (user.shared_info) {\r\n          // Filter by city\r\n          if (\r\n            filters.city &&\r\n            user.shared_info.city?.toLowerCase() !== filters.city.toLowerCase()\r\n          ) {\r\n            return false;\r\n          }\r\n\r\n          // Filter by district\r\n          if (\r\n            filters.district &&\r\n            user.shared_info.district?.toLowerCase() !==\r\n              filters.district.toLowerCase()\r\n          ) {\r\n            return false;\r\n          }\r\n\r\n          // Filter by phone (contains)\r\n          if (\r\n            filters.phone_contains &&\r\n            !user.shared_info.phone?.includes(filters.phone_contains)\r\n          ) {\r\n            return false;\r\n          }\r\n\r\n          // Filter by name (contains)\r\n          if (\r\n            filters.name_contains &&\r\n            !user.shared_info.name\r\n              ?.toLowerCase()\r\n              .includes(filters.name_contains.toLowerCase())\r\n          ) {\r\n            return false;\r\n          }\r\n\r\n          // Filter by address (contains)\r\n          if (\r\n            filters.address_contains &&\r\n            !user.shared_info.address\r\n              ?.toLowerCase()\r\n              .includes(filters.address_contains.toLowerCase())\r\n          ) {\r\n            return false;\r\n          }\r\n\r\n          // Filter by date of birth range\r\n          if (filters.dob_range && user.shared_info.user_dob) {\r\n            const userDob = this.parseDate(user.shared_info.user_dob);\r\n            const fromDob = this.parseDate(filters.dob_range.from);\r\n            const toDob = this.parseDate(filters.dob_range.to);\r\n\r\n            if (userDob < fromDob || userDob > toDob) {\r\n              return false;\r\n            }\r\n          }\r\n\r\n          // Filter by age range (calculated from dob)\r\n          if (filters.age_range && user.shared_info.user_dob) {\r\n            const userAge = this.calculateAge(user.shared_info.user_dob);\r\n            if (\r\n              userAge < filters.age_range.min ||\r\n              userAge > filters.age_range.max\r\n            ) {\r\n              return false;\r\n            }\r\n          }\r\n        } else {\r\n          // If user has no shared_info, filter out based on shared_info requirements\r\n          if (\r\n            filters.city ||\r\n            filters.district ||\r\n            filters.phone_contains ||\r\n            filters.name_contains ||\r\n            filters.address_contains ||\r\n            filters.dob_range ||\r\n            filters.age_range\r\n          ) {\r\n            return false;\r\n          }\r\n        }\r\n\r\n        // Filter by external user ID (contains)\r\n        if (\r\n          filters.external_id_contains &&\r\n          !user.user_external_id?.includes(filters.external_id_contains)\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        // Filter by has shared info\r\n        if (\r\n          filters.has_shared_info !== undefined &&\r\n          !!user.shared_info !== filters.has_shared_info\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        // Filter by has phone\r\n        if (\r\n          filters.has_phone !== undefined &&\r\n          !!user.shared_info?.phone !== filters.has_phone\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        // Filter by has address\r\n        if (\r\n          filters.has_address !== undefined &&\r\n          !!user.shared_info?.address !== filters.has_address\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        return true;\r\n      });\r\n\r\n      console.log(\r\n        `Applied advanced filters: ${allUsers.length} -> ${filteredUsers.length} users`\r\n      );\r\n      return filteredUsers;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      this.handleError(error, \"Failed to get users with advanced filters\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Parse date from dd/MM/yyyy format to Date object\r\n   * Helper method cho các filter date\r\n   */\r\n  private parseDate(dateString: string): Date {\r\n    if (!dateString) return new Date(0);\r\n    const [day, month, year] = dateString.split(\"/\").map(Number);\r\n    return new Date(year, month - 1, day);\r\n  }\r\n\r\n  /**\r\n   * Calculate age from date of birth\r\n   * Helper method cho age filter\r\n   */\r\n  private calculateAge(dobString: string): number {\r\n    if (!dobString) return 0;\r\n    const dob = this.parseDate(dobString);\r\n    const today = new Date();\r\n    let age = today.getFullYear() - dob.getFullYear();\r\n    const monthDiff = today.getMonth() - dob.getMonth();\r\n\r\n    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < dob.getDate())) {\r\n      age--;\r\n    }\r\n\r\n    return age;\r\n  }\r\n\r\n  /**\r\n   * Cập nhật chi tiết người dùng\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/user/update\r\n   * Method: POST\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param request Update user request with user_id and optional fields\r\n   * @returns Promise<boolean> - true if successful\r\n   */\r\n  public async updateUser(\r\n    accessToken: string,\r\n    request: UpdateUserRequest\r\n  ): Promise<boolean> {\r\n    try {\r\n      const result = await this.client.apiPost(\r\n        this.endpoints.user.update,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to update user\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return true;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to update user: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Xóa thông tin người dùng\r\n   * Endpoint: https://openapi.zalo.me/v2.0/oa/deletefollowerinfo\r\n   * Method: POST\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param userId User ID to delete info for\r\n   * @returns Promise<boolean> - true if successful\r\n   *\r\n   * Note: Chỉ xóa thông tin lưu trữ ở phía OA, không thay đổi tài khoản Zalo\r\n   */\r\n  public async deleteUserInfo(\r\n    accessToken: string,\r\n    userId: string\r\n  ): Promise<boolean> {\r\n    try {\r\n      const request: DeleteUserInfoRequest = { user_id: userId };\r\n      const result = await this.client.apiPost(\r\n        this.endpoints.user.delete,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to delete user info\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return true;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to delete user info: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Truy xuất thông tin tùy biến của người dùng\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/user/detail/custominfo\r\n   * Method: GET with data parameter as JSON string\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param request Get custom info request with user_id and optional fields_to_export\r\n   * @returns Promise<UserCustomInfoResponse> - Custom info data\r\n   *\r\n   * Note: Tất cả giá trị trong custom_info đều được trả về dưới dạng string\r\n   */\r\n  public async getUserCustomInfo(\r\n    accessToken: string,\r\n    request: GetUserCustomInfoRequest\r\n  ): Promise<UserCustomInfoResponse> {\r\n    try {\r\n      // Build data object according to API spec\r\n      const dataObject = {\r\n        user_id: request.user_id,\r\n        ...(request.fields_to_export && {\r\n          fields_to_export: request.fields_to_export,\r\n        }),\r\n      };\r\n\r\n      // Send data as JSON string parameter according to API spec\r\n      const params = {\r\n        data: JSON.stringify(dataObject),\r\n      };\r\n\r\n      const result: UserCustomInfoResponse = await this.client.apiGet(\r\n        this.endpoints.user.getCustomInfo,\r\n        accessToken,\r\n        params\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get user custom info\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return result;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get user custom info: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Cập nhật thông tin tùy biến của người dùng\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/user/update/custominfo\r\n   * Method: POST\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param request Update custom info request with user_id and custom_info\r\n   * @returns Promise<boolean> - true if successful\r\n   *\r\n   * Note: Cấu trúc custom_info phụ thuộc vào thiết lập OA\r\n   */\r\n  public async updateUserCustomInfo(\r\n    accessToken: string,\r\n    request: UpdateUserCustomInfoRequest\r\n  ): Promise<boolean> {\r\n    try {\r\n      const result = await this.client.apiPost(\r\n        this.endpoints.user.updateCustomInfo,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to update user custom info\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return true;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to update user custom info: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Lấy danh sách nhãn\r\n   * Endpoint: https://openapi.zalo.me/v2.0/oa/tag/gettagsofoa\r\n   * Method: GET\r\n   *\r\n   * @param accessToken OA access token\r\n   * @returns Promise<string[]> - Array of tag names\r\n   */\r\n  public async getLabels(accessToken: string): Promise<string[]> {\r\n    try {\r\n      const result: GetTagsResponse = await this.client.apiGet(\r\n        this.endpoints.tag.getList,\r\n        accessToken\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get labels\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return result.data || [];\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get labels: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gắn nhãn người dùng\r\n   * Endpoint: https://openapi.zalo.me/v2.0/oa/tag/tagfollower\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param request Tag user request with user_id and tag_name\r\n   * @returns Promise<boolean> - true if successful\r\n   *\r\n   * Note: Nếu tag_name không tồn tại, API sẽ tự động tạo nhãn mới\r\n   */\r\n  public async addTagToUser(\r\n    accessToken: string,\r\n    request: TagUserRequest\r\n  ): Promise<boolean> {\r\n    try {\r\n      const result = await this.client.apiPost(\r\n        this.endpoints.tag.addToUser,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to add tag to user\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return true;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to add tag to user: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gỡ nhãn người dùng\r\n   * Endpoint: https://openapi.zalo.me/v2.0/oa/tag/rmfollowerfromtag\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param request Remove tag request with user_id and tag_name\r\n   * @returns Promise<boolean> - true if successful\r\n   */\r\n  public async removeTagFromUser(\r\n    accessToken: string,\r\n    request: RemoveTagFromUserRequest\r\n  ): Promise<boolean> {\r\n    try {\r\n      const result = await this.client.apiPost(\r\n        this.endpoints.tag.removeFromUser,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to remove tag from user\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return true;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to remove tag from user: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Xóa nhãn\r\n   * Endpoint: https://openapi.zalo.me/v2.0/oa/tag/rmtag\r\n   * Method: POST\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param tagName Tag name to delete\r\n   * @returns Promise<boolean> - true if successful\r\n   */\r\n  public async deleteLabel(\r\n    accessToken: string,\r\n    tagName: string\r\n  ): Promise<boolean> {\r\n    try {\r\n      const request: DeleteTagRequest = { tag_name: tagName };\r\n\r\n      const result = await this.client.apiPost(\r\n        this.endpoints.tag.delete,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to delete label\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return true;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to delete label: ${error.message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get users by tag\r\n   */\r\n  public async getUsersByTag(\r\n    accessToken: string,\r\n    tagName: string,\r\n    offset: number = 0,\r\n    count: number = 50\r\n  ): Promise<UserListResponse> {\r\n    return this.getUserList(accessToken, {\r\n      offset,\r\n      count,\r\n      tag_name: tagName,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Get followers only\r\n   */\r\n  public async getFollowers(\r\n    accessToken: string,\r\n    offset: number = 0,\r\n    count: number = 50\r\n  ): Promise<UserListResponse> {\r\n    return this.getUserList(accessToken, {\r\n      offset,\r\n      count,\r\n      is_follower: true,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Get users by interaction period\r\n   */\r\n  public async getUsersByInteraction(\r\n    accessToken: string,\r\n    period: \"TODAY\" | \"YESTERDAY\" | \"L7D\" | \"L30D\",\r\n    offset: number = 0,\r\n    count: number = 50\r\n  ): Promise<UserListResponse> {\r\n    return this.getUserList(accessToken, {\r\n      offset,\r\n      count,\r\n      last_interaction_period: period,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Bulk tag operations\r\n   */\r\n  public async bulkAddTag(\r\n    accessToken: string,\r\n    userIds: string[],\r\n    tagName: string\r\n  ): Promise<{ success: string[]; failed: string[] }> {\r\n    const results: { success: string[]; failed: string[] } = {\r\n      success: [],\r\n      failed: [],\r\n    };\r\n\r\n    for (const userId of userIds) {\r\n      try {\r\n        await this.addTagToUser(accessToken, {\r\n          user_id: userId,\r\n          tag_name: tagName,\r\n        });\r\n        results.success.push(userId);\r\n      } catch (error) {\r\n        results.failed.push(userId);\r\n      }\r\n    }\r\n\r\n    return results;\r\n  }\r\n\r\n  /**\r\n   * Bulk remove tag operations\r\n   */\r\n  public async bulkRemoveTag(\r\n    accessToken: string,\r\n    userIds: string[],\r\n    tagName: string\r\n  ): Promise<{ success: string[]; failed: string[] }> {\r\n    const results: { success: string[]; failed: string[] } = {\r\n      success: [],\r\n      failed: [],\r\n    };\r\n\r\n    for (const userId of userIds) {\r\n      try {\r\n        await this.removeTagFromUser(accessToken, {\r\n          user_id: userId,\r\n          tag_name: tagName,\r\n        });\r\n        results.success.push(userId);\r\n      } catch (error) {\r\n        results.failed.push(userId);\r\n      }\r\n    }\r\n\r\n    return results;\r\n  }\r\n\r\n  private handleError(error: unknown, message: string): never {\r\n    const errorMessage =\r\n      error instanceof Error ? error.message : \"Unknown error\";\r\n    throw new ZaloSDKError(`${message}: ${errorMessage}`, -1, error);\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport { ZaloSDKError } from \"../types/common\";\r\nimport {\r\n  ZNSMessage,\r\n  ZNSSendResult,\r\n  ZNSUidMessage,\r\n  ZNSUidSendResult,\r\n  ZNSTemplate,\r\n  ZNSTemplateList,\r\n  ZNSTemplateDetails,\r\n  ZNSCreateTemplateRequest,\r\n  ZNSUpdateTemplateRequest,\r\n  ZNSTemplateCreateEditResponse,\r\n  ZNSUploadImageResult,\r\n  ZNSHashPhoneMessage,\r\n  ZNSDevModeMessage,\r\n  ZNSRsaMessage,\r\n  ZNSJourneyMessage,\r\n  ZNSMessageStatusInfo,\r\n  ZNSBatchMessageStatusRequest,\r\n  ZNSBatchMessageStatusResponse,\r\n  ZNSQuotaInfo,\r\n  ZNSAllowedContentTypes,\r\n  ZNSTemplateSampleData,\r\n  ZNSCustomerRatingResponse,\r\n  ZNSOAQualityInfo,\r\n  // Import các type validation components\r\n  ZNSValidationComponent,\r\n  ZNSTitleComponent,\r\n  ZNSParagraphComponent,\r\n  ZNSOTPComponent,\r\n  ZNSTableComponent,\r\n  ZNSTableRow,\r\n  ZNSLogoComponent,\r\n  ZNSImagesComponent,\r\n  ZNSButtonsComponent,\r\n  ZNSButtonItem,\r\n  ZNSPaymentComponent,\r\n  ZNSVoucherComponent,\r\n  ZNSRatingComponent,\r\n  ZNSRatingItem,\r\n  ZNSAttachment,\r\n} from \"../types/zns\";\r\n\r\n// ===== Custom aggregated result types =====\r\nexport interface ZNSAllTemplateDetailsResult {\r\n  error: number;\r\n  message: string;\r\n  data: Array<ZNSTemplateDetails[\"data\"]>;\r\n  metadata: {\r\n    total: number;\r\n  };\r\n}\r\n\r\nexport interface ZNSOAGroupedTemplateDetails {\r\n  accessToken: string;\r\n  templates: Array<ZNSTemplateDetails[\"data\"]>;\r\n  total: number;\r\n}\r\n\r\nexport interface ZNSMultiOAAllTemplateDetailsResult {\r\n  error: number;\r\n  message: string;\r\n  data: ZNSOAGroupedTemplateDetails[];\r\n}\r\n\r\n/**\r\n * Service for Zalo Notification Service (ZNS)\r\n *\r\n * Requirements for using ZNS API:\r\n * - Official Account must be approved and have ZNS sending permission\r\n * - Valid Official Account access token\r\n * - ZNS template must be approved before use\r\n * - Recipient phone number must be in correct format and valid\r\n * - Template data must match defined parameters\r\n * - Comply with message quantity limits according to service package\r\n *\r\n * Reference: https://developers.zalo.me/docs/zalo-notification-service/\r\n */\r\nexport class ZNSService {\r\n  // Zalo ZNS endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    message: {\r\n      sendTemplate: \"https://business.openapi.zalo.me/message/template\",\r\n      sendHashPhone:\r\n        \"https://business.openapi.zalo.me/message/template/hash_phone\",\r\n      sendDev: \"https://business.openapi.zalo.me/message/template/dev\",\r\n      sendRsa: \"https://business.openapi.zalo.me/message/template/rsa\",\r\n      sendJourney: \"https://business.openapi.zalo.me/message/template/journey\",\r\n      sendByUid: \"https://openapi.zalo.me/v3.0/oa/message/template\",\r\n      status: \"https://business.openapi.zalo.me/message/status\",\r\n      quota: \"https://business.openapi.zalo.me/message/quota\",\r\n      templateTag: \"https://business.openapi.zalo.me/message/template-tag\",\r\n    },\r\n    template: {\r\n      list: \"https://business.openapi.zalo.me/template/all\",\r\n      detail: \"https://business.openapi.zalo.me/template/info/v2\",\r\n      create: \"https://business.openapi.zalo.me/template/create\",\r\n      edit: \"https://business.openapi.zalo.me/template/edit\",\r\n      uploadImage: \"https://business.openapi.zalo.me/upload/image\",\r\n      quality: \"https://business.openapi.zalo.me/template/quality\",\r\n      sampleData: \"https://business.openapi.zalo.me/template/sample-data\",\r\n    },\r\n    rating: {\r\n      get: \"https://business.openapi.zalo.me/rating/get\",\r\n    },\r\n    quality: {\r\n      oa: \"https://business.openapi.zalo.me/quality\",\r\n    },\r\n  } as const;\r\n\r\n  constructor(private readonly client: ZaloClient) { }\r\n\r\n  /**\r\n   * Send ZNS message\r\n   * @param accessToken OA access token\r\n   * @param message ZNS message data\r\n   * @returns Send result\r\n   */\r\n  async sendMessage(\r\n    accessToken: string,\r\n    message: ZNSMessage\r\n  ): Promise<ZNSSendResult> {\r\n    try {\r\n      const response = await this.client.apiPost<ZNSSendResult>(\r\n        this.endpoints.message.sendTemplate,\r\n        accessToken,\r\n        message\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to send ZNS message\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send ZNS message with phone hash\r\n   * @param accessToken OA access token\r\n   * @param message ZNS message with hashed phone\r\n   * @returns Send result\r\n   */\r\n  async sendHashPhoneMessage(\r\n    accessToken: string,\r\n    message: ZNSHashPhoneMessage\r\n  ): Promise<ZNSSendResult> {\r\n    try {\r\n      const response = await this.client.apiPost<ZNSSendResult>(\r\n        this.endpoints.message.sendHashPhone,\r\n        accessToken,\r\n        message\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to send ZNS hash phone message\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send ZNS message in development mode\r\n   * @param accessToken OA access token\r\n   * @param message ZNS dev mode message\r\n   * @returns Send result\r\n   */\r\n  async sendDevModeMessage(\r\n    accessToken: string,\r\n    message: ZNSDevModeMessage\r\n  ): Promise<ZNSSendResult> {\r\n    try {\r\n      const response = await this.client.apiPost<ZNSSendResult>(\r\n        this.endpoints.message.sendDev,\r\n        accessToken,\r\n        message\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to send ZNS dev mode message\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send ZNS message with RSA encryption\r\n   * @param accessToken OA access token\r\n   * @param message ZNS RSA message\r\n   * @returns Send result\r\n   */\r\n  async sendRsaMessage(\r\n    accessToken: string,\r\n    message: ZNSRsaMessage\r\n  ): Promise<ZNSSendResult> {\r\n    try {\r\n      const response = await this.client.apiPost<ZNSSendResult>(\r\n        this.endpoints.message.sendRsa,\r\n        accessToken,\r\n        message\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to send ZNS RSA message\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send ZNS journey message\r\n   * @param accessToken OA access token\r\n   * @param message ZNS journey message\r\n   * @returns Send result\r\n   */\r\n  async sendJourneyMessage(\r\n    accessToken: string,\r\n    message: ZNSJourneyMessage\r\n  ): Promise<ZNSSendResult> {\r\n    try {\r\n      const response = await this.client.apiPost<ZNSSendResult>(\r\n        this.endpoints.message.sendJourney,\r\n        accessToken,\r\n        message\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to send ZNS journey message\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send ZBS/ZNS Template Message by UID\r\n   * Gửi tin nhắn template theo UID người dùng thay vì số điện thoại\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param message ZNS UID message\r\n   * @returns Send result với message_id và user_id\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/message/template\r\n   *\r\n   * Lưu ý:\r\n   * - App phải đã bật quyền gửi tin và thông báo qua OA\r\n   * - Tin Giao dịch: Sử dụng template Tag 1 (TRANSACTION) hoặc Tag 2 (CUSTOMER_CARE)\r\n   * - Tin Hậu mãi: Sử dụng template Tag 3 (PROMOTION)\r\n   * - Có hạn mức nhận tin theo chính sách của Zalo\r\n   *\r\n   * Example:\r\n   * ```typescript\r\n   * const result = await znsService.sendMessageByUid(accessToken, {\r\n   *   user_id: \"3485904339058348590\",\r\n   *   template_id: \"433555\",\r\n   *   template_data: {\r\n   *     customer: \"Nguyễn Thị Hoàng Anh\",\r\n   *     amount: \"100000\",\r\n   *     // ... các params khác theo template\r\n   *   }\r\n   * });\r\n   * ```\r\n   */\r\n  async sendMessageByUid(\r\n    accessToken: string,\r\n    message: ZNSUidMessage\r\n  ): Promise<ZNSUidSendResult> {\r\n    try {\r\n      // Validate input\r\n      if (!message.user_id) {\r\n        throw new ZaloSDKError(\"user_id là bắt buộc\", 400);\r\n      }\r\n      if (!message.template_id) {\r\n        throw new ZaloSDKError(\"template_id là bắt buộc\", 400);\r\n      }\r\n      if (!message.template_data || typeof message.template_data !== 'object') {\r\n        throw new ZaloSDKError(\"template_data phải là object\", 400);\r\n      }\r\n\r\n      const response = await this.client.apiPost<ZNSUidSendResult>(\r\n        this.endpoints.message.sendByUid,\r\n        accessToken,\r\n        message\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to send ZNS message by UID\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ZNS message status\r\n   * @param accessToken OA access token\r\n   * @param messageId Message ID\r\n   * @returns Message status info\r\n   *\r\n   * Response data:\r\n   * - delivery_time: Thời gian thiết bị nhận được thông báo\r\n   * - status: Trạng thái thông báo\r\n   *   * -1: The message does not exist\r\n   *   * 0: The message is pushed successfully to Zalo server but has not yet delivered to user's phone\r\n   *   * 1: The message was delivered to the user's phone\r\n   * - message: Mô tả trạng thái thông báo\r\n   */\r\n  async getMessageStatus(\r\n    accessToken: string,\r\n    messageId: string\r\n  ): Promise<ZNSMessageStatusInfo> {\r\n    try {\r\n      const response = await this.client.apiGet<ZNSMessageStatusInfo>(\r\n        this.endpoints.message.status,\r\n        accessToken,\r\n        { message_id: messageId }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get ZNS message status\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ZNS batch message status (custom implementation)\r\n   * @param accessToken OA access token\r\n   * @param messageIds Array of message IDs\r\n   * @returns Batch message status info\r\n   *\r\n   * This is a custom implementation that calls the single message status API\r\n   * multiple times to get status for multiple messages. Since Zalo API doesn't\r\n   * provide a native batch endpoint, this method provides convenience by:\r\n   * - Making concurrent API calls for better performance\r\n   * - Handling errors gracefully (failed requests return error status)\r\n   * - Returning consistent response format\r\n   */\r\n  async getBatchMessageStatus(\r\n    accessToken: string,\r\n    messageIds: string[]\r\n  ): Promise<ZNSBatchMessageStatusResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!Array.isArray(messageIds) || messageIds.length === 0) {\r\n        throw new ZaloSDKError(\"messageIds phải là array và không được rỗng\", 400);\r\n      }\r\n\r\n      if (messageIds.length > 100) {\r\n        throw new ZaloSDKError(\"Không được kiểm tra quá 100 message cùng lúc\", 400);\r\n      }\r\n\r\n      // Remove duplicates\r\n      const uniqueMessageIds = [...new Set(messageIds)];\r\n\r\n      // Make concurrent requests for all message IDs\r\n      const statusPromises = uniqueMessageIds.map(async (messageId) => {\r\n        try {\r\n          const status = await this.getMessageStatus(accessToken, messageId);\r\n          return {\r\n            messageId,\r\n            success: true,\r\n            data: status.data\r\n          };\r\n        } catch (error) {\r\n          return {\r\n            messageId,\r\n            success: false,\r\n            error: error instanceof ZaloSDKError ? error.message : \"Unknown error\"\r\n          };\r\n        }\r\n      });\r\n\r\n      // Wait for all requests to complete\r\n      const results = await Promise.all(statusPromises);\r\n\r\n      // Build response data\r\n      const responseData: { [messageId: string]: any } = {};\r\n      let hasErrors = false;\r\n\r\n      results.forEach(result => {\r\n        if (result.success && result.data) {\r\n          responseData[result.messageId] = result.data;\r\n        } else {\r\n          hasErrors = true;\r\n          responseData[result.messageId] = {\r\n            delivery_time: \"\",\r\n            message: result.error || \"Failed to get status\",\r\n            status: -1\r\n          };\r\n        }\r\n      });\r\n\r\n      return {\r\n        error: hasErrors ? 1 : 0,\r\n        message: hasErrors ? \"Some messages failed to get status\" : \"Success\",\r\n        data: responseData\r\n      };\r\n\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get batch ZNS message status\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ZNS quota information\r\n   * @param accessToken OA access token\r\n   * @returns Quota information\r\n   *\r\n   * Response data:\r\n   * - dailyQuota: Số thông báo ZNS OA được gửi trong 1 ngày\r\n   * - remainingQuota: Số thông báo ZNS OA được gửi trong ngày còn lại\r\n   * - dailyQuotaPromotion: Số tin ZNS hậu mãi OA được gửi trong ngày (null từ 1/11)\r\n   * - remainingQuotaPromotion: Số tin ZNS hậu mãi còn lại OA được gửi trong ngày (null từ 1/11)\r\n   * - monthlyPromotionQuota: Số tin ZNS hậu mãi OA được gửi trong tháng\r\n   * - remainingMonthlyPromotionQuota: Số tin ZNS hậu mãi còn lại OA được gửi trong tháng\r\n   * - estimatedNextMonthPromotionQuota: Số tin ZNS hậu mãi dự kiến mà OA có thể gửi trong tháng tiếp theo\r\n   */\r\n  async getQuotaInfo(accessToken: string): Promise<ZNSQuotaInfo> {\r\n    try {\r\n      const response = await this.client.apiGet<ZNSQuotaInfo>(\r\n        this.endpoints.message.quota,\r\n        accessToken\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get ZNS quota info\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ZNS allowed content types\r\n   * @param accessToken OA access token\r\n   * @returns Allowed content types\r\n   *\r\n   * Response data:\r\n   * - Mảng các loại nội dung mà OA có thể gửi:\r\n   *   * \"TRANSACTION\": Giao dịch (cấp độ 1)\r\n   *   * \"CUSTOMER_CARE\": Chăm sóc khách hàng (cấp độ 2)\r\n   *   * \"PROMOTION\": Hậu mãi (cấp độ 3)\r\n   * - Dựa theo chất lượng gửi ZNS của OA, Zalo sẽ tự động điều chỉnh loại nội dung OA được gửi\r\n   */\r\n  async getAllowedContentTypes(\r\n    accessToken: string\r\n  ): Promise<ZNSAllowedContentTypes> {\r\n    try {\r\n      const response = await this.client.apiGet<ZNSAllowedContentTypes>(\r\n        this.endpoints.message.templateTag,\r\n        accessToken\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(\r\n        error,\r\n        \"Failed to get ZNS allowed content types\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ZNS template list\r\n   * @param accessToken OA access token\r\n   * @param offset Offset for pagination (template được tạo gần nhất có thứ tự 0)\r\n   * @param limit Limit for pagination (tối đa 100)\r\n   * @param status Template status filter (optional)\r\n   * @returns Template list\r\n   *\r\n   * Status values:\r\n   * - 1: ENABLE templates\r\n   * - 2: PENDING_REVIEW templates\r\n   * - 3: REJECT templates\r\n   * - 4: DISABLE templates\r\n   * - undefined: All templates\r\n   */\r\n  async getTemplateList(\r\n    accessToken: string,\r\n    offset: number = 0,\r\n    limit: number = 10,\r\n    status?: 1 | 2 | 3 | 4\r\n  ): Promise<ZNSTemplateList> {\r\n    try {\r\n      // Validate limit (max 100)\r\n      if (limit > 100) {\r\n        throw new ZaloSDKError(\"Limit không được vượt quá 100\", 400);\r\n      }\r\n\r\n      // Prepare query parameters\r\n      const params: any = { offset, limit };\r\n      if (status !== undefined) {\r\n        params.status = status;\r\n      }\r\n\r\n      const response = await this.client.apiGet<ZNSTemplateList>(\r\n        this.endpoints.template.list,\r\n        accessToken,\r\n        params\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get ZNS template list\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ZNS template details\r\n   * @param accessToken OA access token\r\n   * @param templateId Template ID\r\n   * @returns Template details\r\n   *\r\n   * Response data:\r\n   * - templateId: ID của template\r\n   * - templateName: Tên của template\r\n   * - status: Trạng thái template (ENABLE, PENDING_REVIEW, DELETE, REJECT, DISABLE)\r\n   * - reason: Lý do template có trạng thái hiện tại\r\n   * - listParams: Danh sách các thuộc tính của template\r\n   * - listButtons: Danh sách các buttons/CTAs của template\r\n   * - timeout: Thời gian timeout của template\r\n   * - previewUrl: Đường dẫn đến bản xem trước của template\r\n   * - templateQuality: Chất lượng template (null từ 10/12)\r\n   * - templateTag: Loại nội dung (TRANSACTION, CUSTOMER_CARE, PROMOTION)\r\n   * - price: Đơn giá của template\r\n   */\r\n  async getTemplateDetails(\r\n    accessToken: string,\r\n    templateId: string\r\n  ): Promise<ZNSTemplateDetails> {\r\n    try {\r\n      const response = await this.client.apiGet<ZNSTemplateDetails>(\r\n        this.endpoints.template.detail,\r\n        accessToken,\r\n        { template_id: templateId }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get ZNS template details\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Custom: Lấy tất cả template kèm thông tin chi tiết cho 1 accessToken\r\n   * - Tự động phân trang để lấy đủ tổng số template\r\n   * - Lấy chi tiết song song theo batch để tối ưu thời gian\r\n   */\r\n  async getAllTemplatesWithDetails(\r\n    accessToken: string,\r\n    status?: 1 | 2 | 3 | 4\r\n  ): Promise<ZNSAllTemplateDetailsResult> {\r\n    try {\r\n      const limitPerPage = 100;\r\n      let offset = 0;\r\n      const templateIds: string[] = [];\r\n      let total = 0;\r\n\r\n      // Paginate through template list\r\n      // eslint-disable-next-line no-constant-condition\r\n      while (true) {\r\n        const listResp = await this.getTemplateList(accessToken, offset, limitPerPage, status);\r\n        const items = listResp.data || [];\r\n        if (listResp.metadata && typeof listResp.metadata.total === \"number\") {\r\n          total = listResp.metadata.total;\r\n        }\r\n\r\n        for (const item of items) {\r\n          if (!templateIds.includes(item.templateId)) {\r\n            templateIds.push(item.templateId);\r\n          }\r\n        }\r\n\r\n        if (items.length < limitPerPage || templateIds.length >= total) {\r\n          break;\r\n        }\r\n        offset += limitPerPage;\r\n      }\r\n\r\n      if (templateIds.length === 0) {\r\n        return { error: 0, message: \"Success\", data: [], metadata: { total } };\r\n      }\r\n\r\n      // Fetch details in batches to avoid burst\r\n      const batchSize = 10;\r\n      const detailResults: Array<ZNSTemplateDetails[\"data\"]> = [];\r\n      let hasErrors = false;\r\n\r\n      for (let i = 0; i < templateIds.length; i += batchSize) {\r\n        const batchIds = templateIds.slice(i, i + batchSize);\r\n        const settled = await Promise.allSettled(\r\n          batchIds.map((id) => this.getTemplateDetails(accessToken, id))\r\n        );\r\n\r\n        for (const res of settled) {\r\n          if (res.status === \"fulfilled\" && res.value && res.value.data) {\r\n            detailResults.push(res.value.data);\r\n          } else {\r\n            hasErrors = true;\r\n          }\r\n        }\r\n      }\r\n\r\n      return {\r\n        error: hasErrors ? 1 : 0,\r\n        message: hasErrors ? \"Some template details failed to fetch\" : \"Success\",\r\n        data: detailResults,\r\n        metadata: { total },\r\n      };\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get all templates with details\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Custom: Lấy tất cả template chi tiết cho nhiều accessToken (nhiều OA)\r\n   * Đầu ra nhóm theo từng accessToken\r\n   */\r\n  async getAllTemplatesWithDetailsByTokens(\r\n    accessTokens: string[],\r\n    status?: 1 | 2 | 3 | 4\r\n  ): Promise<ZNSMultiOAAllTemplateDetailsResult> {\r\n    try {\r\n      const uniqueTokens = Array.from(new Set(accessTokens.filter((t) => t && t.trim().length > 0)));\r\n      if (uniqueTokens.length === 0) {\r\n        return { error: 0, message: \"Success\", data: [] };\r\n      }\r\n\r\n      const settled = await Promise.allSettled(\r\n        uniqueTokens.map((token) => this.getAllTemplatesWithDetails(token, status))\r\n      );\r\n\r\n      const groups: ZNSOAGroupedTemplateDetails[] = [];\r\n      let hasErrors = false;\r\n\r\n      settled.forEach((res, index) => {\r\n        const token = uniqueTokens[index];\r\n        if (res.status === \"fulfilled\") {\r\n          const payload = res.value;\r\n          groups.push({\r\n            accessToken: token,\r\n            templates: payload.data,\r\n            total: payload.metadata.total,\r\n          });\r\n          if (payload.error !== 0) {\r\n            hasErrors = true;\r\n          }\r\n        } else {\r\n          hasErrors = true;\r\n          groups.push({ accessToken: token, templates: [], total: 0 });\r\n        }\r\n      });\r\n\r\n      return {\r\n        error: hasErrors ? 1 : 0,\r\n        message: hasErrors ? \"Some OA groups failed to fetch\" : \"Success\",\r\n        data: groups,\r\n      };\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get templates with details by tokens\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ZNS template sample data\r\n   * @param accessToken OA access token\r\n   * @param templateId Template ID\r\n   * @returns Template sample data\r\n   *\r\n   * Response data:\r\n   * - Chứa tham số và dữ liệu mẫu của template\r\n   * - Ví dụ: { \"balance_debt\": 2000, \"due_date\": \"01/01/1970\", \"customer_name\": \"customer_name_sample\" }\r\n   */\r\n  async getTemplateSampleData(\r\n    accessToken: string,\r\n    templateId: string\r\n  ): Promise<ZNSTemplateSampleData> {\r\n    try {\r\n      const response = await this.client.apiGet<ZNSTemplateSampleData>(\r\n        this.endpoints.template.sampleData,\r\n        accessToken,\r\n        { template_id: templateId }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(\r\n        error,\r\n        \"Failed to get ZNS template sample data\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get customer rating information\r\n   * @param accessToken OA access token\r\n   * @param templateId Template ID\r\n   * @param fromTime Start time (timestamp in milliseconds)\r\n   * @param toTime End time (timestamp in milliseconds)\r\n   * @param offset Position of first rating to return\r\n   * @param limit Maximum number of ratings to return\r\n   * @returns Customer rating information\r\n   *\r\n   * Lưu ý:\r\n   * - Chỉ có thể lấy thông tin đánh giá từ template đánh giá dịch vụ được tạo bởi ứng dụng\r\n   * - Access token phải ứng với template ID được tạo bởi app và OA\r\n   * - Thời gian theo định dạng timestamp (millisecond)\r\n   */\r\n  async getCustomerRating(\r\n    accessToken: string,\r\n    templateId: string,\r\n    fromTime: number,\r\n    toTime: number,\r\n    offset: number,\r\n    limit: number\r\n  ): Promise<ZNSCustomerRatingResponse> {\r\n    try {\r\n      const response = await this.client.apiGet<ZNSCustomerRatingResponse>(\r\n        this.endpoints.rating.get,\r\n        accessToken,\r\n        {\r\n          template_id: templateId,\r\n          from_time: fromTime,\r\n          to_time: toTime,\r\n          offset,\r\n          limit,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get customer rating\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get OA ZNS sending quality information\r\n   * @param accessToken OA access token\r\n   * @returns OA quality information\r\n   *\r\n   * Response data:\r\n   * - oaCurrentQuality: Chất lượng gửi ZNS trong 48 giờ gần nhất\r\n   * - oa7dayQuality: Chất lượng gửi ZNS trong 7 ngày gần nhất\r\n   *\r\n   * Quality levels:\r\n   * - HIGH: Mức độ chất lượng tốt\r\n   * - MEDIUM: Mức độ chất lượng trung bình\r\n   * - LOW: Mức độ chất lượng kém\r\n   * - UNDEFINED: Chưa được xác định (OA không gửi ZNS trong khung thời gian đánh giá)\r\n   */\r\n  async getOAQuality(accessToken: string): Promise<ZNSOAQualityInfo> {\r\n    try {\r\n      const response = await this.client.apiGet<ZNSOAQualityInfo>(\r\n        this.endpoints.quality.oa,\r\n        accessToken\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to get OA quality information\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Create ZNS template\r\n   * @param accessToken OA access token\r\n   * @param templateData Template creation data theo chuẩn Zalo API\r\n   * @returns Created template response\r\n   *\r\n   * API: POST https://business.openapi.zalo.me/template/create\r\n   */\r\n  async createTemplate(\r\n    accessToken: string,\r\n    templateData: ZNSCreateTemplateRequest\r\n  ): Promise<ZNSTemplateCreateEditResponse> {\r\n    try {\r\n      // Comprehensive validation\r\n      this.validateZNSTemplateRequest(templateData);\r\n\r\n      const response = await this.client.apiPost<ZNSTemplateCreateEditResponse>(\r\n        this.endpoints.template.create,\r\n        accessToken,\r\n        templateData\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to create ZNS template\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Edit ZNS template (chỉnh sửa template có trạng thái REJECT)\r\n   * @param accessToken OA access token\r\n   * @param templateData Template edit data theo chuẩn Zalo API\r\n   * @returns Edited template response\r\n   *\r\n   * Lưu ý:\r\n   * - Chỉ có thể chỉnh sửa template có trạng thái REJECT\r\n   * - Template sau khi chỉnh sửa sẽ chuyển về trạng thái PENDING_REVIEW\r\n   * - Daily quota: 100 requests/ngày\r\n   * - Cần quyền \"Quản lý tài sản\"\r\n   *\r\n   * API: POST https://business.openapi.zalo.me/template/edit\r\n   */\r\n  async updateTemplate(\r\n    accessToken: string,\r\n    templateData: ZNSUpdateTemplateRequest\r\n  ): Promise<ZNSTemplateCreateEditResponse> {\r\n    try {\r\n      // Validate template_id first\r\n      if (!templateData.template_id) {\r\n        throw new ZaloSDKError(\"template_id là bắt buộc\", 400);\r\n      }\r\n\r\n      // Comprehensive validation (same as createTemplate)\r\n      this.validateZNSTemplateRequest(templateData);\r\n\r\n      const response = await this.client.apiPost<ZNSTemplateCreateEditResponse>(\r\n        this.endpoints.template.edit,\r\n        accessToken,\r\n        templateData\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to edit ZNS template\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Upload image for ZNS template\r\n   * @param accessToken OA access token\r\n   * @param imageFile Image file (Buffer or ReadableStream)\r\n   * @param filename Filename with extension\r\n   * @returns Upload result with media_id\r\n   *\r\n   * Lưu ý:\r\n   * - Định dạng hỗ trợ: JPG, PNG\r\n   * - Dung lượng tối đa: 500KB\r\n   * - Hạn mức: 5000 ảnh/tháng/app\r\n   * - Logo: PNG, 400x96px\r\n   * - Hình ảnh: JPG/PNG, tỉ lệ 16:9\r\n   * - Cần quyền \"Quản lý tài sản\"\r\n   */\r\n  async uploadImage(\r\n    accessToken: string,\r\n    imageFile: Buffer | NodeJS.ReadableStream,\r\n    filename: string = \"image.jpg\"\r\n  ): Promise<ZNSUploadImageResult> {\r\n    try {\r\n      // Validate file extension\r\n      const allowedExtensions = [\".jpg\", \".jpeg\", \".png\"];\r\n      const fileExtension = filename\r\n        .toLowerCase()\r\n        .substring(filename.lastIndexOf(\".\"));\r\n\r\n      if (!allowedExtensions.includes(fileExtension)) {\r\n        throw new ZaloSDKError(\"File phải có định dạng JPG hoặc PNG\", 400);\r\n      }\r\n\r\n      // Validate file size if it's a Buffer (max 500KB)\r\n      if (Buffer.isBuffer(imageFile)) {\r\n        const maxSize = 500 * 1024; // 500KB\r\n        if (imageFile.length > maxSize) {\r\n          throw new ZaloSDKError(\"Dung lượng file không được vượt quá 500KB\", 400);\r\n        }\r\n      }\r\n\r\n      const response = await this.client.apiUploadFile<ZNSUploadImageResult>(\r\n        this.endpoints.template.uploadImage,\r\n        accessToken,\r\n        imageFile,\r\n        filename\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleZNSError(error, \"Failed to upload ZNS image\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Comprehensive validation for ZNS template request\r\n   */\r\n  public validateZNSTemplateRequest(templateData: ZNSCreateTemplateRequest): void {\r\n    // 1. Basic required fields validation\r\n    this.validateBasicFields(templateData);\r\n\r\n    // 2. Template type and tag compatibility\r\n    this.validateTemplateTypeTagCompatibility(templateData.template_type, templateData.tag);\r\n\r\n    // 3. Layout structure validation\r\n    this.validateLayoutStructure(templateData.template_type, templateData.layout);\r\n\r\n    // 4. Params validation if provided\r\n    if (templateData.params && templateData.params.length > 0) {\r\n      this.validateParams(templateData.params, templateData.layout);\r\n    }\r\n\r\n    // 5. Note validation if provided\r\n    if (templateData.note) {\r\n      if (templateData.note.length < 1 || templateData.note.length > 400) {\r\n        throw new ZaloSDKError(\"note phải có độ dài từ 1 đến 400 ký tự\", 400);\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate basic required fields\r\n   */\r\n  private validateBasicFields(templateData: ZNSCreateTemplateRequest): void {\r\n    if (!templateData.template_name) {\r\n      throw new ZaloSDKError(\"template_name là bắt buộc\", 400);\r\n    }\r\n    if (templateData.template_name.length < 10 || templateData.template_name.length > 60) {\r\n      throw new ZaloSDKError(\"template_name phải có độ dài từ 10 đến 60 ký tự\", 400);\r\n    }\r\n\r\n    if (!templateData.template_type) {\r\n      throw new ZaloSDKError(\"template_type là bắt buộc\", 400);\r\n    }\r\n    if (![1, 2, 3, 4, 5].includes(templateData.template_type)) {\r\n      throw new ZaloSDKError(\"template_type phải là 1, 2, 3, 4 hoặc 5\", 400);\r\n    }\r\n\r\n    if (!templateData.tag) {\r\n      throw new ZaloSDKError(\"tag là bắt buộc\", 400);\r\n    }\r\n    if (![\"1\", \"2\", \"3\"].includes(templateData.tag)) {\r\n      throw new ZaloSDKError(\"tag phải là '1', '2' hoặc '3'\", 400);\r\n    }\r\n\r\n    if (!templateData.layout) {\r\n      throw new ZaloSDKError(\"layout là bắt buộc\", 400);\r\n    }\r\n    if (!Array.isArray(templateData.layout) && typeof templateData.layout !== 'object') {\r\n      throw new ZaloSDKError(\"layout phải là array hoặc object\", 400);\r\n    }\r\n\r\n    if (!templateData.tracking_id) {\r\n      throw new ZaloSDKError(\"tracking_id là bắt buộc\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate template_type and tag compatibility\r\n   */\r\n  private validateTemplateTypeTagCompatibility(templateType: number, tag: string): void {\r\n    const compatibility: Record<number, string[]> = {\r\n      1: [\"1\", \"2\", \"3\"], // ZNS tùy chỉnh\r\n      2: [\"1\"],           // ZNS xác thực\r\n      3: [\"1\"],           // ZNS yêu cầu thanh toán\r\n      4: [\"1\", \"2\", \"3\"], // ZNS voucher\r\n      5: [\"2\"]            // ZNS đánh giá dịch vụ\r\n    };\r\n\r\n    if (!compatibility[templateType]?.includes(tag)) {\r\n      throw new ZaloSDKError(`template_type ${templateType} không tương thích với tag ${tag}`, 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate layout structure based on template type\r\n   */\r\n  private validateLayoutStructure(templateType: number, layout: ZNSValidationComponent[] | Record<string, any>): void {\r\n    // Handle both array format (Zalo API) and object format (our types)\r\n    let headerComponents: ZNSValidationComponent[] = [];\r\n    let bodyComponents: ZNSValidationComponent[] = [];\r\n    let footerComponents: ZNSValidationComponent[] = [];\r\n    let allComponents: ZNSValidationComponent[] = [];\r\n\r\n    if (Array.isArray(layout)) {\r\n      // Array format - direct from Zalo API docs\r\n      headerComponents = layout.filter(item => this.isHeaderComponent(item));\r\n      bodyComponents = layout.filter(item => this.isBodyComponent(item));\r\n      footerComponents = layout.filter(item => this.isFooterComponent(item));\r\n      allComponents = layout;\r\n    } else if (layout && typeof layout === 'object') {\r\n      // Object format - our structured type\r\n      if (layout.header?.components) {\r\n        headerComponents = layout.header.components;\r\n      }\r\n      if (layout.body?.components) {\r\n        bodyComponents = layout.body.components;\r\n      }\r\n      if (layout.footer?.components) {\r\n        footerComponents = layout.footer.components;\r\n      }\r\n      allComponents = [...headerComponents, ...bodyComponents, ...footerComponents];\r\n    } else {\r\n      throw new ZaloSDKError(\"layout phải là array hoặc object có cấu trúc header/body/footer\", 400);\r\n    }\r\n\r\n    switch (templateType) {\r\n      case 1: // ZNS tùy chỉnh\r\n        this.validateCustomTemplateLayout(headerComponents, bodyComponents, footerComponents);\r\n        break;\r\n      case 2: // ZNS xác thực\r\n        this.validateAuthTemplateLayout(headerComponents, bodyComponents, footerComponents);\r\n        break;\r\n      case 3: // ZNS yêu cầu thanh toán\r\n        this.validatePaymentTemplateLayout(headerComponents, bodyComponents, footerComponents);\r\n        break;\r\n      case 4: // ZNS voucher\r\n        this.validateVoucherTemplateLayout(headerComponents, bodyComponents, footerComponents);\r\n        break;\r\n      case 5: // ZNS đánh giá dịch vụ\r\n        this.validateRatingTemplateLayout(headerComponents, bodyComponents, footerComponents);\r\n        break;\r\n    }\r\n\r\n    // Validate individual components\r\n    allComponents.forEach(component => this.validateComponent(component));\r\n  }\r\n\r\n  /**\r\n   * Check if component belongs to header\r\n   */\r\n  private isHeaderComponent(component: ZNSValidationComponent): boolean {\r\n    return 'LOGO' in component || 'IMAGES' in component;\r\n  }\r\n\r\n  /**\r\n   * Check if component belongs to body\r\n   */\r\n  private isBodyComponent(component: ZNSValidationComponent): boolean {\r\n    return 'TITLE' in component || 'PARAGRAPH' in component || 'TABLE' in component ||\r\n      'OTP' in component || 'PAYMENT' in component || 'VOUCHER' in component || 'RATING' in component;\r\n  }\r\n\r\n  /**\r\n   * Check if component belongs to footer\r\n   */\r\n  private isFooterComponent(component: ZNSValidationComponent): boolean {\r\n    return 'BUTTONS' in component;\r\n  }\r\n\r\n  /**\r\n   * Validate custom template layout (type 1)\r\n   */\r\n  private validateCustomTemplateLayout(header: ZNSValidationComponent[], body: ZNSValidationComponent[], footer: ZNSValidationComponent[]): void {\r\n    // Header: Logo OR Image (exactly 1)\r\n    if (header.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS tùy chỉnh phải có đúng 1 component trong header (Logo hoặc Image)\", 400);\r\n    }\r\n\r\n    // Body: Title (1) + Paragraph (0-4) + Table (0-1)\r\n    const titles = body.filter(c => 'TITLE' in c);\r\n    const paragraphs = body.filter(c => 'PARAGRAPH' in c);\r\n    const tables = body.filter(c => 'TABLE' in c);\r\n\r\n    if (titles.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS tùy chỉnh phải có đúng 1 component TITLE\", 400);\r\n    }\r\n    if (paragraphs.length > 4) {\r\n      throw new ZaloSDKError(\"ZNS tùy chỉnh không được có quá 4 component PARAGRAPH\", 400);\r\n    }\r\n    if (tables.length > 1) {\r\n      throw new ZaloSDKError(\"ZNS tùy chỉnh không được có quá 1 component TABLE\", 400);\r\n    }\r\n\r\n    // Footer: Buttons (0-2)\r\n    if (footer.length > 2) {\r\n      throw new ZaloSDKError(\"ZNS tùy chỉnh không được có quá 2 button\", 400);\r\n    }\r\n\r\n    // If has image, must have at least 1 button\r\n    const hasImage = header.some(c => 'IMAGES' in c);\r\n    if (hasImage && footer.length === 0) {\r\n      throw new ZaloSDKError(\"ZNS có component Image phải có ít nhất 1 button\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate auth template layout (type 2)\r\n   */\r\n  private validateAuthTemplateLayout(header: ZNSValidationComponent[], body: ZNSValidationComponent[], footer: ZNSValidationComponent[]): void {\r\n    // Header: Logo only (1)\r\n    if (header.length !== 1 || !('LOGO' in header[0])) {\r\n      throw new ZaloSDKError(\"ZNS xác thực phải có đúng 1 component LOGO trong header\", 400);\r\n    }\r\n\r\n    // Body: OTP (1) + Paragraph (1)\r\n    const otps = body.filter(c => 'OTP' in c);\r\n    const paragraphs = body.filter(c => 'PARAGRAPH' in c);\r\n\r\n    if (otps.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS xác thực phải có đúng 1 component OTP\", 400);\r\n    }\r\n    if (paragraphs.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS xác thực phải có đúng 1 component PARAGRAPH\", 400);\r\n    }\r\n\r\n    // Footer: None\r\n    if (footer.length > 0) {\r\n      throw new ZaloSDKError(\"ZNS xác thực không được có component trong footer\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate payment template layout (type 3)\r\n   */\r\n  private validatePaymentTemplateLayout(header: ZNSValidationComponent[], body: ZNSValidationComponent[], footer: ZNSValidationComponent[]): void {\r\n    // Header: Logo only (1)\r\n    if (header.length !== 1 || !('LOGO' in header[0])) {\r\n      throw new ZaloSDKError(\"ZNS yêu cầu thanh toán phải có đúng 1 component LOGO trong header\", 400);\r\n    }\r\n\r\n    // Body: Title (1) + Paragraph (0-4) + Table (0-1) + Payment (1)\r\n    const titles = body.filter(c => 'TITLE' in c);\r\n    const paragraphs = body.filter(c => 'PARAGRAPH' in c);\r\n    const tables = body.filter(c => 'TABLE' in c);\r\n    const payments = body.filter(c => 'PAYMENT' in c);\r\n\r\n    if (titles.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS yêu cầu thanh toán phải có đúng 1 component TITLE\", 400);\r\n    }\r\n    if (paragraphs.length > 4) {\r\n      throw new ZaloSDKError(\"ZNS yêu cầu thanh toán không được có quá 4 component PARAGRAPH\", 400);\r\n    }\r\n    if (tables.length > 1) {\r\n      throw new ZaloSDKError(\"ZNS yêu cầu thanh toán không được có quá 1 component TABLE\", 400);\r\n    }\r\n    if (payments.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS yêu cầu thanh toán phải có đúng 1 component PAYMENT\", 400);\r\n    }\r\n\r\n    // Footer: Buttons (0-2)\r\n    if (footer.length > 2) {\r\n      throw new ZaloSDKError(\"ZNS yêu cầu thanh toán không được có quá 2 button\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate voucher template layout (type 4)\r\n   */\r\n  private validateVoucherTemplateLayout(header: ZNSValidationComponent[], body: ZNSValidationComponent[], footer: ZNSValidationComponent[]): void {\r\n    // Header: Logo OR Image (exactly 1)\r\n    if (header.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS voucher phải có đúng 1 component trong header (Logo hoặc Image)\", 400);\r\n    }\r\n\r\n    // Body: Title (1) + Paragraph (0-4) + Table (0-1) + Voucher (1)\r\n    const titles = body.filter(c => 'TITLE' in c);\r\n    const paragraphs = body.filter(c => 'PARAGRAPH' in c);\r\n    const tables = body.filter(c => 'TABLE' in c);\r\n    const vouchers = body.filter(c => 'VOUCHER' in c);\r\n\r\n    if (titles.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS voucher phải có đúng 1 component TITLE\", 400);\r\n    }\r\n    if (paragraphs.length > 4) {\r\n      throw new ZaloSDKError(\"ZNS voucher không được có quá 4 component PARAGRAPH\", 400);\r\n    }\r\n    if (tables.length > 1) {\r\n      throw new ZaloSDKError(\"ZNS voucher không được có quá 1 component TABLE\", 400);\r\n    }\r\n    if (vouchers.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS voucher phải có đúng 1 component VOUCHER\", 400);\r\n    }\r\n\r\n    // Footer: Buttons (0-2)\r\n    if (footer.length > 2) {\r\n      throw new ZaloSDKError(\"ZNS voucher không được có quá 2 button\", 400);\r\n    }\r\n\r\n    // If has image, must have at least 1 button, max 2\r\n    const hasImage = header.some(c => 'IMAGES' in c);\r\n    if (hasImage && footer.length === 0) {\r\n      throw new ZaloSDKError(\"ZNS voucher có component Image phải có ít nhất 1 button\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate rating template layout (type 5)\r\n   */\r\n  private validateRatingTemplateLayout(header: ZNSValidationComponent[], body: ZNSValidationComponent[], footer: ZNSValidationComponent[]): void {\r\n    // Header: Logo only (1)\r\n    if (header.length !== 1 || !('LOGO' in header[0])) {\r\n      throw new ZaloSDKError(\"ZNS đánh giá dịch vụ phải có đúng 1 component LOGO trong header\", 400);\r\n    }\r\n\r\n    // Body: Title (1) + Paragraph (0-1) + Rating (1)\r\n    const titles = body.filter(c => 'TITLE' in c);\r\n    const paragraphs = body.filter(c => 'PARAGRAPH' in c);\r\n    const ratings = body.filter(c => 'RATING' in c);\r\n\r\n    if (titles.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS đánh giá dịch vụ phải có đúng 1 component TITLE\", 400);\r\n    }\r\n    if (paragraphs.length > 1) {\r\n      throw new ZaloSDKError(\"ZNS đánh giá dịch vụ không được có quá 1 component PARAGRAPH\", 400);\r\n    }\r\n    if (ratings.length !== 1) {\r\n      throw new ZaloSDKError(\"ZNS đánh giá dịch vụ phải có đúng 1 component RATING\", 400);\r\n    }\r\n\r\n    // Footer: Buttons (0-2)\r\n    if (footer.length > 2) {\r\n      throw new ZaloSDKError(\"ZNS đánh giá dịch vụ không được có quá 2 button\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate individual component content\r\n   */\r\n  private validateComponent(component: ZNSValidationComponent): void {\r\n    if ('TITLE' in component) {\r\n      this.validateTitleComponent(component.TITLE);\r\n    }\r\n    if ('PARAGRAPH' in component) {\r\n      this.validateParagraphComponent(component.PARAGRAPH);\r\n    }\r\n    if ('OTP' in component) {\r\n      this.validateOTPComponent(component.OTP);\r\n    }\r\n    if ('TABLE' in component) {\r\n      this.validateTableComponent(component.TABLE);\r\n    }\r\n    if ('LOGO' in component) {\r\n      this.validateLogoComponent(component.LOGO);\r\n    }\r\n    if ('IMAGES' in component) {\r\n      this.validateImagesComponent(component.IMAGES);\r\n    }\r\n    if ('BUTTONS' in component) {\r\n      this.validateButtonsComponent(component.BUTTONS);\r\n    }\r\n    if ('PAYMENT' in component) {\r\n      this.validatePaymentComponent(component.PAYMENT);\r\n    }\r\n    if ('VOUCHER' in component) {\r\n      this.validateVoucherComponent(component.VOUCHER);\r\n    }\r\n    if ('RATING' in component) {\r\n      this.validateRatingComponent(component.RATING);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate TITLE component\r\n   */\r\n  private validateTitleComponent(title: ZNSTitleComponent): void {\r\n    if (!title.value || typeof title.value !== 'string') {\r\n      throw new ZaloSDKError(\"TITLE component phải có field value kiểu string\", 400);\r\n    }\r\n    if (title.value.length < 9 || title.value.length > 65) {\r\n      throw new ZaloSDKError(\"TITLE value phải có độ dài từ 9 đến 65 ký tự\", 400);\r\n    }\r\n    // Check max 4 params\r\n    const paramCount = (title.value.match(/\\{\\{[^}]+\\}\\}/g) || []).length;\r\n    if (paramCount > 4) {\r\n      throw new ZaloSDKError(\"TITLE component không được có quá 4 params\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate PARAGRAPH component\r\n   */\r\n  private validateParagraphComponent(paragraph: ZNSParagraphComponent): void {\r\n    if (!paragraph.value || typeof paragraph.value !== 'string') {\r\n      throw new ZaloSDKError(\"PARAGRAPH component phải có field value kiểu string\", 400);\r\n    }\r\n    if (paragraph.value.length < 9 || paragraph.value.length > 400) {\r\n      throw new ZaloSDKError(\"PARAGRAPH value phải có độ dài từ 9 đến 400 ký tự\", 400);\r\n    }\r\n    // Check max 10 params\r\n    const paramCount = (paragraph.value.match(/\\{\\{[^}]+\\}\\}/g) || []).length;\r\n    if (paramCount > 10) {\r\n      throw new ZaloSDKError(\"PARAGRAPH component không được có quá 10 params\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate OTP component\r\n   */\r\n  private validateOTPComponent(otp: ZNSOTPComponent): void {\r\n    if (!otp.value || typeof otp.value !== 'string') {\r\n      throw new ZaloSDKError(\"OTP component phải có field value kiểu string\", 400);\r\n    }\r\n    if (otp.value.length < 1 || otp.value.length > 10) {\r\n      throw new ZaloSDKError(\"OTP value phải có độ dài từ 1 đến 10 ký tự\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate TABLE component\r\n   */\r\n  private validateTableComponent(table: ZNSTableComponent): void {\r\n    if (!table.rows || !Array.isArray(table.rows)) {\r\n      throw new ZaloSDKError(\"TABLE component phải có field rows kiểu array\", 400);\r\n    }\r\n    if (table.rows.length < 2 || table.rows.length > 8) {\r\n      throw new ZaloSDKError(\"TABLE rows phải có từ 2 đến 8 objects\", 400);\r\n    }\r\n\r\n    table.rows.forEach((row: ZNSTableRow, index: number) => {\r\n      if (!row.title || typeof row.title !== 'string') {\r\n        throw new ZaloSDKError(`TABLE row ${index + 1} phải có field title kiểu string`, 400);\r\n      }\r\n      if (row.title.length < 3 || row.title.length > 36) {\r\n        throw new ZaloSDKError(`TABLE row ${index + 1} title phải có độ dài từ 3 đến 36 ký tự`, 400);\r\n      }\r\n      if (!row.value || typeof row.value !== 'string') {\r\n        throw new ZaloSDKError(`TABLE row ${index + 1} phải có field value kiểu string`, 400);\r\n      }\r\n      if (row.value.length < 3 || row.value.length > 90) {\r\n        throw new ZaloSDKError(`TABLE row ${index + 1} value phải có độ dài từ 3 đến 90 ký tự`, 400);\r\n      }\r\n      if (row.row_type !== undefined && ![0, 1, 2, 3, 4, 5].includes(row.row_type)) {\r\n        throw new ZaloSDKError(`TABLE row ${index + 1} row_type phải là 0, 1, 2, 3, 4 hoặc 5`, 400);\r\n      }\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Validate LOGO component\r\n   */\r\n  private validateLogoComponent(logo: ZNSLogoComponent): void {\r\n    if (!logo.light || !logo.dark) {\r\n      throw new ZaloSDKError(\"LOGO component phải có cả light và dark attachment\", 400);\r\n    }\r\n    this.validateAttachment(logo.light, \"LOGO light\");\r\n    this.validateAttachment(logo.dark, \"LOGO dark\");\r\n  }\r\n\r\n  /**\r\n   * Validate IMAGES component\r\n   */\r\n  private validateImagesComponent(images: ZNSImagesComponent): void {\r\n    if (!images.items || !Array.isArray(images.items)) {\r\n      throw new ZaloSDKError(\"IMAGES component phải có field items kiểu array\", 400);\r\n    }\r\n    if (images.items.length < 1 || images.items.length > 3) {\r\n      throw new ZaloSDKError(\"IMAGES items phải có từ 1 đến 3 attachments\", 400);\r\n    }\r\n    images.items.forEach((item: ZNSAttachment, index: number) => {\r\n      this.validateAttachment(item, `IMAGES item ${index + 1}`);\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Validate BUTTONS component\r\n   */\r\n  private validateButtonsComponent(buttons: ZNSButtonsComponent): void {\r\n    if (!buttons.items || !Array.isArray(buttons.items)) {\r\n      throw new ZaloSDKError(\"BUTTONS component phải có field items kiểu array\", 400);\r\n    }\r\n    if (buttons.items.length < 1 || buttons.items.length > 2) {\r\n      throw new ZaloSDKError(\"BUTTONS items phải có từ 1 đến 2 objects\", 400);\r\n    }\r\n\r\n    buttons.items.forEach((button: ZNSButtonItem, index: number) => {\r\n      if (!button.type || ![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].includes(button.type)) {\r\n        throw new ZaloSDKError(`BUTTON ${index + 1} type phải là 1-12`, 400);\r\n      }\r\n      if (!button.title || typeof button.title !== 'string') {\r\n        throw new ZaloSDKError(`BUTTON ${index + 1} phải có field title kiểu string`, 400);\r\n      }\r\n      if (button.title.length < 5 || button.title.length > 30) {\r\n        throw new ZaloSDKError(`BUTTON ${index + 1} title phải có độ dài từ 5 đến 30 ký tự`, 400);\r\n      }\r\n      if (!button.content || typeof button.content !== 'string') {\r\n        throw new ZaloSDKError(`BUTTON ${index + 1} phải có field content kiểu string`, 400);\r\n      }\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Validate PAYMENT component\r\n   */\r\n  private validatePaymentComponent(payment: ZNSPaymentComponent): void {\r\n    if (!payment.bank_code || typeof payment.bank_code !== 'string') {\r\n      throw new ZaloSDKError(\"PAYMENT component phải có field bank_code kiểu string\", 400);\r\n    }\r\n    if (!payment.account_name || typeof payment.account_name !== 'string') {\r\n      throw new ZaloSDKError(\"PAYMENT component phải có field account_name kiểu string\", 400);\r\n    }\r\n    if (payment.account_name.length < 1 || payment.account_name.length > 100) {\r\n      throw new ZaloSDKError(\"PAYMENT account_name phải có độ dài từ 1 đến 100 ký tự\", 400);\r\n    }\r\n    if (!payment.bank_account || typeof payment.bank_account !== 'string') {\r\n      throw new ZaloSDKError(\"PAYMENT component phải có field bank_account kiểu string\", 400);\r\n    }\r\n    if (payment.bank_account.length < 1 || payment.bank_account.length > 100) {\r\n      throw new ZaloSDKError(\"PAYMENT bank_account phải có độ dài từ 1 đến 100 ký tự\", 400);\r\n    }\r\n    if (!payment.amount) {\r\n      throw new ZaloSDKError(\"PAYMENT component phải có field amount\", 400);\r\n    }\r\n    if (payment.note && (payment.note.length < 1 || payment.note.length > 90)) {\r\n      throw new ZaloSDKError(\"PAYMENT note phải có độ dài từ 1 đến 90 ký tự\", 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate VOUCHER component\r\n   */\r\n  private validateVoucherComponent(voucher: ZNSVoucherComponent): void {\r\n    if (!voucher.name || typeof voucher.name !== 'string') {\r\n      throw new ZaloSDKError(\"VOUCHER component phải có field name kiểu string\", 400);\r\n    }\r\n    if (voucher.name.length < 1 || voucher.name.length > 30) {\r\n      throw new ZaloSDKError(\"VOUCHER name phải có độ dài từ 1 đến 30 ký tự\", 400);\r\n    }\r\n    if (!voucher.condition || typeof voucher.condition !== 'string') {\r\n      throw new ZaloSDKError(\"VOUCHER component phải có field condition kiểu string\", 400);\r\n    }\r\n    if (voucher.condition.length < 1 || voucher.condition.length > 40) {\r\n      throw new ZaloSDKError(\"VOUCHER condition phải có độ dài từ 1 đến 40 ký tự\", 400);\r\n    }\r\n    if (!voucher.end_date || typeof voucher.end_date !== 'string') {\r\n      throw new ZaloSDKError(\"VOUCHER component phải có field end_date kiểu string\", 400);\r\n    }\r\n    if (!voucher.voucher_code || typeof voucher.voucher_code !== 'string') {\r\n      throw new ZaloSDKError(\"VOUCHER component phải có field voucher_code kiểu string\", 400);\r\n    }\r\n    if (voucher.voucher_code.length < 1 || voucher.voucher_code.length > 25) {\r\n      throw new ZaloSDKError(\"VOUCHER voucher_code phải có độ dài từ 1 đến 25 ký tự\", 400);\r\n    }\r\n\r\n  }\r\n\r\n  /**\r\n   * Validate RATING component\r\n   */\r\n  private validateRatingComponent(rating: ZNSRatingComponent): void {\r\n    if (!rating.items || !Array.isArray(rating.items)) {\r\n      throw new ZaloSDKError(\"RATING component phải có field items kiểu array\", 400);\r\n    }\r\n    if (rating.items.length !== 5) {\r\n      throw new ZaloSDKError(\"RATING items phải có đúng 5 objects\", 400);\r\n    }\r\n\r\n    rating.items.forEach((item: ZNSRatingItem, index: number) => {\r\n      const starValue = (index + 1) as 1 | 2 | 3 | 4 | 5;\r\n      if (item.star !== starValue) {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} phải có star = ${starValue}`, 400);\r\n      }\r\n      if (!item.title || typeof item.title !== 'string') {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} phải có field title kiểu string`, 400);\r\n      }\r\n      if (item.title.length < 1 || item.title.length > 50) {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} title phải có độ dài từ 1 đến 50 ký tự`, 400);\r\n      }\r\n      if (item.question && (item.question.length < 1 || item.question.length > 100)) {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} question phải có độ dài từ 1 đến 100 ký tự`, 400);\r\n      }\r\n      if (item.answers && Array.isArray(item.answers)) {\r\n        if (item.answers.length < 1 || item.answers.length > 5) {\r\n          throw new ZaloSDKError(`RATING item ${index + 1} answers phải có từ 1 đến 5 câu trả lời`, 400);\r\n        }\r\n        item.answers.forEach((answer: string, answerIndex: number) => {\r\n          if (!answer || answer.length < 1 || answer.length > 50) {\r\n            throw new ZaloSDKError(`RATING item ${index + 1} answer ${answerIndex + 1} phải có độ dài từ 1 đến 50 ký tự`, 400);\r\n          }\r\n        });\r\n      }\r\n      if (!item.thanks || typeof item.thanks !== 'string') {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} phải có field thanks kiểu string`, 400);\r\n      }\r\n      if (item.thanks.length < 1 || item.thanks.length > 100) {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} thanks phải có độ dài từ 1 đến 100 ký tự`, 400);\r\n      }\r\n      if (!item.description || typeof item.description !== 'string') {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} phải có field description kiểu string`, 400);\r\n      }\r\n      if (item.description.length < 1 || item.description.length > 200) {\r\n        throw new ZaloSDKError(`RATING item ${index + 1} description phải có độ dài từ 1 đến 200 ký tự`, 400);\r\n      }\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Validate attachment object\r\n   */\r\n  private validateAttachment(attachment: ZNSAttachment, context: string): void {\r\n    if (!attachment.type || !attachment.media_id) {\r\n      throw new ZaloSDKError(`${context} phải có type và media_id`, 400);\r\n    }\r\n    if (attachment.type !== \"IMAGE\") {\r\n      throw new ZaloSDKError(`${context} type phải là \"IMAGE\"`, 400);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate params array\r\n   */\r\n  private validateParams(params: Array<{\r\n    name: string;\r\n    type: string;\r\n    sample_value: string;\r\n  }>, layout: ZNSValidationComponent[] | Record<string, any>): void {\r\n    const paramTypeConstraints: Record<string, number> = {\r\n      \"1\": 30,  // Tên khách hàng\r\n      \"2\": 15,  // Số điện thoại\r\n      \"3\": 200, // Địa chỉ\r\n      \"4\": 30,  // Mã số\r\n      \"5\": 30,  // Nhãn tùy chỉnh\r\n      \"6\": 30,  // Trạng thái giao dịch\r\n      \"7\": 50,  // Thông tin liên hệ\r\n      \"8\": 5,   // Giới tính / Danh xưng\r\n      \"9\": 200, // Tên sản phẩm / Thương hiệu\r\n      \"10\": 20, // Số lượng / Số tiền\r\n      \"11\": 20, // Thời gian\r\n      \"12\": 10, // OTP\r\n      \"13\": 200, // URL\r\n      \"14\": 12, // Tiền tệ (VNĐ)\r\n      \"15\": 90  // Bank transfer note\r\n    };\r\n\r\n    // Get all params used in layout\r\n    const layoutString = JSON.stringify(layout);\r\n    const usedParams = new Set<string>();\r\n    const paramMatches = layoutString.match(/\\{\\{([^}]+)\\}\\}/g);\r\n    if (paramMatches) {\r\n      paramMatches.forEach(match => {\r\n        const paramName = match.replace(/[{}]/g, '');\r\n        usedParams.add(paramName);\r\n      });\r\n    }\r\n\r\n    // Validate each param\r\n    params.forEach((param: {\r\n      name: string;\r\n      type: string;\r\n      sample_value: string;\r\n    }, index: number) => {\r\n      if (!param.name || typeof param.name !== 'string') {\r\n        throw new ZaloSDKError(`Param ${index + 1} phải có field name kiểu string`, 400);\r\n      }\r\n      if (!param.type || typeof param.type !== 'string') {\r\n        throw new ZaloSDKError(`Param ${index + 1} phải có field type kiểu string`, 400);\r\n      }\r\n      if (!Object.keys(paramTypeConstraints).includes(param.type)) {\r\n        throw new ZaloSDKError(`Param ${index + 1} type phải là 1-15`, 400);\r\n      }\r\n      if (!param.sample_value || typeof param.sample_value !== 'string') {\r\n        throw new ZaloSDKError(`Param ${index + 1} phải có field sample_value kiểu string`, 400);\r\n      }\r\n\r\n      // Validate sample_value length based on type\r\n      const maxLength = paramTypeConstraints[param.type];\r\n      if (param.sample_value.length > maxLength) {\r\n        throw new ZaloSDKError(`Param ${index + 1} sample_value không được vượt quá ${maxLength} ký tự cho type ${param.type}`, 400);\r\n      }\r\n\r\n      // Remove param from used set if found\r\n      usedParams.delete(param.name);\r\n    });\r\n\r\n    // Check if all used params are defined\r\n    if (usedParams.size > 0) {\r\n      throw new ZaloSDKError(`Các param sau được sử dụng trong layout nhưng chưa được định nghĩa: ${Array.from(usedParams).join(', ')}`, 400);\r\n    }\r\n  }\r\n\r\n  private handleZNSError(error: any, defaultMessage: string): ZaloSDKError {\r\n    if (error.response?.data) {\r\n      const errorData = error.response.data;\r\n      return new ZaloSDKError(\r\n        `${defaultMessage}: ${errorData.message || errorData.error || \"Unknown error\"\r\n        }`,\r\n        errorData.error || -1,\r\n        errorData\r\n      );\r\n    }\r\n    return new ZaloSDKError(`${defaultMessage}: ${error.message || \"Unknown error\"}`, -1, error);\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  GroupTextMessage,\r\n  GroupImageMessage,\r\n  GroupFileMessage,\r\n  GroupStickerMessage,\r\n  GroupMentionMessage,\r\n  GroupMessageResult,\r\n  GroupInfo,\r\n  GroupMember,\r\n} from \"../types/group\";\r\nimport { ZaloSDKError } from \"../types/common\";\r\n\r\n/**\r\n * Interface cho từng tin nhắn trong danh sách tin nhắn group\r\n */\r\nexport interface GroupMessageItem {\r\n  /** Loại tin nhắn */\r\n  type: \"text\" | \"image\" | \"file\" | \"sticker\" | \"mention\";\r\n  /** Nội dung tin nhắn (cho text/mention message) */\r\n  text?: string;\r\n  /** URL hình ảnh (cho image message) */\r\n  imageUrl?: string;\r\n  /** Attachment ID (cho image message) */\r\n  attachmentId?: string;\r\n  /** Caption cho hình ảnh (tối đa 2000 ký tự) */\r\n  caption?: string;\r\n  /** Token file (cho file message) */\r\n  fileToken?: string;\r\n  /** Tên file (cho file message) */\r\n  fileName?: string;\r\n  /** Sticker ID (cho sticker message) */\r\n  stickerId?: string;\r\n  /** Danh sách mentions (cho mention message) */\r\n  mentions?: Array<{\r\n    user_id: string;\r\n    offset: number;\r\n    length: number;\r\n  }>;\r\n  /** Delay sau khi gửi tin nhắn này (milliseconds) */\r\n  delay?: number;\r\n}\r\n\r\n/**\r\n * Interface cho request gửi danh sách tin nhắn tới 1 group\r\n */\r\nexport interface SendMessageListToGroupRequest {\r\n  /** Access token của Official Account */\r\n  accessToken: string;\r\n  /** Group ID */\r\n  groupId: string;\r\n  /** Danh sách tin nhắn */\r\n  messages: GroupMessageItem[];\r\n  /** Delay mặc định giữa các tin nhắn (milliseconds) */\r\n  defaultDelay?: number;\r\n  /** Callback function để tracking tiến trình */\r\n  onProgress?: (progress: GroupMessageProgressInfo) => void;\r\n}\r\n\r\n/**\r\n * Interface cho thông tin tiến trình từng tin nhắn\r\n */\r\nexport interface GroupMessageProgressInfo {\r\n  /** Group ID */\r\n  groupId: string;\r\n  /** Index của tin nhắn trong danh sách (bắt đầu từ 0) */\r\n  messageIndex: number;\r\n  /** Tổng số tin nhắn */\r\n  totalMessages: number;\r\n  /** Loại tin nhắn */\r\n  messageType: string;\r\n  /** Trạng thái: 'started' | 'completed' | 'failed' */\r\n  status: 'started' | 'completed' | 'failed';\r\n  /** Kết quả gửi tin nhắn (nếu đã hoàn thành) */\r\n  result?: GroupMessageResult;\r\n  /** Thông tin lỗi (nếu thất bại) */\r\n  error?: string;\r\n  /** Thời gian bắt đầu */\r\n  startTime: number;\r\n  /** Thời gian kết thúc (nếu đã hoàn thành) */\r\n  endTime?: number;\r\n}\r\n\r\n/**\r\n * Interface cho response gửi danh sách tin nhắn tới 1 group\r\n */\r\nexport interface SendMessageListToGroupResponse {\r\n  /** Group ID */\r\n  groupId: string;\r\n  /** Tổng số tin nhắn */\r\n  totalMessages: number;\r\n  /** Số tin nhắn gửi thành công */\r\n  successfulMessages: number;\r\n  /** Số tin nhắn gửi thất bại */\r\n  failedMessages: number;\r\n  /** Chi tiết kết quả từng tin nhắn */\r\n  messageResults: Array<{\r\n    /** Index của tin nhắn trong danh sách */\r\n    messageIndex: number;\r\n    /** Loại tin nhắn */\r\n    messageType: string;\r\n    /** Trạng thái gửi */\r\n    success: boolean;\r\n    /** Kết quả chi tiết */\r\n    result?: GroupMessageResult;\r\n    /** Thông tin lỗi nếu thất bại */\r\n    error?: string;\r\n    /** Thời gian bắt đầu gửi */\r\n    startTime: number;\r\n    /** Thời gian kết thúc */\r\n    endTime: number;\r\n    /** Thời gian thực hiện (milliseconds) */\r\n    duration: number;\r\n  }>;\r\n  /** Tổng thời gian thực hiện (milliseconds) */\r\n  totalDuration: number;\r\n}\r\n\r\n/**\r\n * Interface cho request gửi danh sách tin nhắn tới nhiều groups\r\n */\r\nexport interface SendMessageListToMultipleGroupsRequest {\r\n  /** Access token của Official Account */\r\n  accessToken: string;\r\n  /** Danh sách group IDs */\r\n  groupIds: string[];\r\n  /** Danh sách tin nhắn */\r\n  messages: GroupMessageItem[];\r\n  /** Delay mặc định giữa các tin nhắn (milliseconds) */\r\n  defaultDelay?: number;\r\n  /** Delay giữa các groups (milliseconds) để tránh rate limit */\r\n  delayBetweenGroups?: number;\r\n  /** Callback function để tracking tiến trình */\r\n  onProgress?: (progress: MultipleGroupsProgressInfo) => void;\r\n}\r\n\r\n/**\r\n * Interface cho thông tin tiến trình từng group\r\n */\r\nexport interface MultipleGroupsProgressInfo {\r\n  /** Group ID hiện tại */\r\n  groupId: string;\r\n  /** Index của group trong danh sách (bắt đầu từ 0) */\r\n  groupIndex: number;\r\n  /** Tổng số groups */\r\n  totalGroups: number;\r\n  /** Trạng thái: 'started' | 'completed' | 'failed' */\r\n  status: 'started' | 'completed' | 'failed';\r\n  /** Kết quả gửi tin nhắn cho group này (nếu đã hoàn thành) */\r\n  result?: SendMessageListToGroupResponse;\r\n  /** Thông tin lỗi (nếu thất bại) */\r\n  error?: string;\r\n  /** Thời gian bắt đầu */\r\n  startTime: number;\r\n  /** Thời gian kết thúc (nếu đã hoàn thành) */\r\n  endTime?: number;\r\n}\r\n\r\n/**\r\n * Interface cho response gửi danh sách tin nhắn tới nhiều groups\r\n */\r\nexport interface SendMessageListToMultipleGroupsResponse {\r\n  /** Tổng số groups */\r\n  totalGroups: number;\r\n  /** Số groups gửi thành công */\r\n  successfulGroups: number;\r\n  /** Số groups gửi thất bại */\r\n  failedGroups: number;\r\n  /** Chi tiết kết quả từng group */\r\n  groupResults: Array<{\r\n    /** Group ID */\r\n    groupId: string;\r\n    /** Index của group trong danh sách */\r\n    groupIndex: number;\r\n    /** Trạng thái gửi cho group này */\r\n    success: boolean;\r\n    /** Kết quả chi tiết gửi tin nhắn */\r\n    messageListResult?: SendMessageListToGroupResponse;\r\n    /** Thông tin lỗi nếu thất bại */\r\n    error?: string;\r\n    /** Thời gian bắt đầu gửi */\r\n    startTime: number;\r\n    /** Thời gian kết thúc */\r\n    endTime: number;\r\n    /** Thời gian thực hiện (milliseconds) */\r\n    duration: number;\r\n  }>;\r\n  /** Tổng thời gian thực hiện (milliseconds) */\r\n  totalDuration: number;\r\n  /** Thống kê tin nhắn tổng cộng */\r\n  messageStats: {\r\n    /** Tổng số tin nhắn đã gửi thành công */\r\n    totalSuccessfulMessages: number;\r\n    /** Tổng số tin nhắn thất bại */\r\n    totalFailedMessages: number;\r\n    /** Tổng số tin nhắn */\r\n    totalMessages: number;\r\n  };\r\n}\r\n\r\n/**\r\n * Interface cho tin nhắn cá nhân hóa cho từng group\r\n */\r\nexport interface PersonalizedGroupMessage {\r\n  /** Group ID */\r\n  groupId: string;\r\n  /** Danh sách tin nhắn riêng cho group này */\r\n  messages: GroupMessageItem[];\r\n}\r\n\r\n/**\r\n * Interface cho request gửi tin nhắn cá nhân hóa tới nhiều groups\r\n */\r\nexport interface SendPersonalizedMessageToMultipleGroupsRequest {\r\n  /** Access token của Official Account */\r\n  accessToken: string;\r\n  /** Danh sách tin nhắn cá nhân hóa cho từng group */\r\n  personalizedMessages: PersonalizedGroupMessage[];\r\n  /** Delay mặc định giữa các tin nhắn (milliseconds) */\r\n  defaultDelay?: number;\r\n  /** Delay giữa các groups (milliseconds) để tránh rate limit */\r\n  delayBetweenGroups?: number;\r\n  /** Callback function để tracking tiến trình */\r\n  onProgress?: (progress: PersonalizedGroupsProgressInfo) => void;\r\n}\r\n\r\n/**\r\n * Interface cho thông tin tiến trình gửi tin nhắn cá nhân hóa\r\n */\r\nexport interface PersonalizedGroupsProgressInfo {\r\n  /** Group ID hiện tại */\r\n  groupId: string;\r\n  /** Index của group trong danh sách (bắt đầu từ 0) */\r\n  groupIndex: number;\r\n  /** Tổng số groups */\r\n  totalGroups: number;\r\n  /** Trạng thái: 'started' | 'completed' | 'failed' */\r\n  status: 'started' | 'completed' | 'failed';\r\n  /** Kết quả gửi tin nhắn cho group này (nếu đã hoàn thành) */\r\n  result?: SendMessageListToGroupResponse;\r\n  /** Thông tin lỗi (nếu thất bại) */\r\n  error?: string;\r\n  /** Thời gian bắt đầu */\r\n  startTime: number;\r\n  /** Thời gian kết thúc (nếu đã hoàn thành) */\r\n  endTime?: number;\r\n}\r\n\r\n/**\r\n * Interface cho response gửi tin nhắn cá nhân hóa tới nhiều groups\r\n */\r\nexport interface SendPersonalizedMessageToMultipleGroupsResponse {\r\n  /** Tổng số groups */\r\n  totalGroups: number;\r\n  /** Số groups gửi thành công */\r\n  successfulGroups: number;\r\n  /** Số groups gửi thất bại */\r\n  failedGroups: number;\r\n  /** Chi tiết kết quả từng group */\r\n  groupResults: Array<{\r\n    /** Group ID */\r\n    groupId: string;\r\n    /** Index của group trong danh sách */\r\n    groupIndex: number;\r\n    /** Trạng thái gửi cho group này */\r\n    success: boolean;\r\n    /** Kết quả chi tiết gửi tin nhắn */\r\n    messageListResult?: SendMessageListToGroupResponse;\r\n    /** Thông tin lỗi nếu thất bại */\r\n    error?: string;\r\n    /** Thời gian bắt đầu gửi */\r\n    startTime: number;\r\n    /** Thời gian kết thúc */\r\n    endTime: number;\r\n    /** Thời gian thực hiện (milliseconds) */\r\n    duration: number;\r\n  }>;\r\n  /** Tổng thời gian thực hiện (milliseconds) */\r\n  totalDuration: number;\r\n  /** Thống kê tin nhắn tổng cộng */\r\n  messageStats: {\r\n    /** Tổng số tin nhắn đã gửi thành công */\r\n    totalSuccessfulMessages: number;\r\n    /** Tổng số tin nhắn thất bại */\r\n    totalFailedMessages: number;\r\n    /** Tổng số tin nhắn */\r\n    totalMessages: number;\r\n  };\r\n}\r\n\r\n/**\r\n * Service for handling Zalo Official Account Group Message Framework (GMF) APIs\r\n *\r\n * CONDITIONS FOR USING ZALO GMF:\r\n *\r\n * 1. OPT-IN CONDITIONS FOR SENDING GROUP MESSAGES:\r\n *    - OA must have the group_id of the chat group\r\n *    - OA must be added to the chat group by group admin\r\n *    - OA must have permission to send messages in the group (granted by group admin)\r\n *    - Chat group must be active (not locked or disbanded)\r\n *\r\n * 2. ACCESS PERMISSIONS:\r\n *    - Application needs to be granted group chat management permissions\r\n *    - Access token must have \"manage_group\" scope or equivalent\r\n *    - OA must be authenticated and have active status\r\n *\r\n * 3. LIMITS AND CONSTRAINTS:\r\n *    - Can only send messages to groups that OA has joined\r\n *    - Cannot send messages to private groups that OA hasn't been invited to\r\n *    - Must comply with Zalo's message sending frequency limits\r\n *    - Message content must comply with Zalo's content policy\r\n *\r\n * 4. SUPPORTED MESSAGE TYPES:\r\n *    - Text message: Plain text messages\r\n *    - Image message: Image messages (JPG, PNG, GIF - max 5MB)\r\n *    - File message: File attachments (max 25MB)\r\n *    - Sticker message: Stickers from Zalo collection\r\n *    - Mention message: Tag/mention specific members\r\n *\r\n * 5. TECHNICAL REQUIREMENTS:\r\n *    - Use HTTPS for all API calls\r\n *    - Content-Type: application/json for text/mention messages\r\n *    - Content-Type: multipart/form-data for file/image uploads\r\n */\r\nexport class GroupMessageService {\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    // Group message endpoints\r\n    message: {\r\n      group: \"https://openapi.zalo.me/v3.0/oa/group/message\",\r\n    },\r\n    // Group management endpoints\r\n    group: {\r\n      getInfo: \"https://openapi.zalo.me/v3.0/oa/group/getinfo\",\r\n      getMembers: \"https://openapi.zalo.me/v3.0/oa/group/getmembers\",\r\n    },\r\n  };\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Send text message to group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param message Text message content\r\n   * @returns Send result\r\n   */\r\n  async sendTextMessage(\r\n    accessToken: string,\r\n    groupId: string,\r\n    message: GroupTextMessage\r\n  ): Promise<GroupMessageResult> {\r\n    try {\r\n      const response = await this.client.apiPost<GroupMessageResult>(\r\n        this.endpoints.message.group,\r\n        accessToken,\r\n        {\r\n          recipient: {\r\n            group_id: groupId,\r\n          },\r\n          message: {\r\n            text: message.text,\r\n          },\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupMessageError(\r\n        error,\r\n        \"Failed to send text message to group\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send image message to group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param message Image message content\r\n   * @returns Send result\r\n   */\r\n  async sendImageMessage(\r\n    accessToken: string,\r\n    groupId: string,\r\n    message: GroupImageMessage\r\n  ): Promise<GroupMessageResult> {\r\n    try {\r\n      // Validate that either imageUrl or attachmentId is provided\r\n      if (!message.imageUrl && !message.attachmentId) {\r\n        throw new Error(\"Either imageUrl or attachmentId must be provided\");\r\n      }\r\n\r\n\r\n      // Validate caption length\r\n      if (message.caption && message.caption.length > 2000) {\r\n        throw new Error(\"Caption cannot exceed 2000 characters\");\r\n      }\r\n\r\n      // Prepare attachment payload\r\n      const attachmentPayload = {\r\n        type: \"template\",\r\n        payload: {\r\n          template_type: \"media\",\r\n          elements: [\r\n            {\r\n              media_type: \"image\",\r\n              ...(message.attachmentId\r\n                ? { attachment_id: message.attachmentId }\r\n                : { url: message.imageUrl }),\r\n            },\r\n          ],\r\n        },\r\n      };\r\n\r\n      // Prepare message payload with correct structure\r\n      const messagePayload: {\r\n        attachment: typeof attachmentPayload;\r\n        text?: string;\r\n      } = {\r\n        attachment: attachmentPayload,\r\n      };\r\n\r\n      // Add text caption if provided - must be at same level as attachment\r\n      if (message.caption) {\r\n        messagePayload.text = message.caption;\r\n      }\r\n\r\n      const response = await this.client.apiPost<GroupMessageResult>(\r\n        this.endpoints.message.group,\r\n        accessToken,\r\n        {\r\n          recipient: {\r\n            group_id: groupId,\r\n          },\r\n          message: messagePayload,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupMessageError(\r\n        error,\r\n        \"Failed to send image message to group\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send file message to group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param message File message content\r\n   * @returns Send result\r\n   */\r\n  async sendFileMessage(\r\n    accessToken: string,\r\n    groupId: string,\r\n    message: GroupFileMessage\r\n  ): Promise<GroupMessageResult> {\r\n    try {\r\n      const response = await this.client.apiPost<GroupMessageResult>(\r\n        this.endpoints.message.group,\r\n        accessToken,\r\n        {\r\n          recipient: {\r\n            group_id: groupId,\r\n          },\r\n          message: {\r\n            attachment: {\r\n              type: \"file\",\r\n              payload: {\r\n                token: message.fileToken,\r\n              },\r\n            },\r\n          },\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupMessageError(\r\n        error,\r\n        \"Failed to send file message to group\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send sticker message to group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param message Sticker message content\r\n   * @returns Send result\r\n   */\r\n  async sendStickerMessage(\r\n    accessToken: string,\r\n    groupId: string,\r\n    message: GroupStickerMessage\r\n  ): Promise<GroupMessageResult> {\r\n    try {\r\n      const response = await this.client.apiPost<GroupMessageResult>(\r\n        this.endpoints.message.group,\r\n        accessToken,\r\n        {\r\n          recipient: {\r\n            group_id: groupId,\r\n          },\r\n          message: {\r\n            attachment: {\r\n              type: \"template\",\r\n              payload: {\r\n                template_type: \"media\",\r\n                elements: [\r\n                  {\r\n                    media_type: \"sticker\",\r\n                    attachment_id: message.stickerId,\r\n                  },\r\n                ],\r\n              },\r\n            },\r\n          },\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupMessageError(\r\n        error,\r\n        \"Failed to send sticker message to group\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Send mention message to group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param message Mention message content\r\n   * @returns Send result\r\n   */\r\n  async sendMentionMessage(\r\n    accessToken: string,\r\n    groupId: string,\r\n    message: GroupMentionMessage\r\n  ): Promise<GroupMessageResult> {\r\n    try {\r\n      const response = await this.client.apiPost<GroupMessageResult>(\r\n        this.endpoints.message.group,\r\n        accessToken,\r\n        {\r\n          recipient: {\r\n            group_id: groupId,\r\n          },\r\n          message: {\r\n            text: message.text,\r\n            mention: message.mentions,\r\n          },\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupMessageError(\r\n        error,\r\n        \"Failed to send mention message to group\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get group information\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @returns Group information\r\n   */\r\n  async getGroupInfo(accessToken: string, groupId: string): Promise<GroupInfo> {\r\n    try {\r\n      const response = await this.client.apiGet<GroupInfo>(\r\n        this.endpoints.group.getInfo,\r\n        accessToken,\r\n        {\r\n          group_id: groupId,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupMessageError(\r\n        error,\r\n        \"Failed to get group information\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get group members\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param offset Offset for pagination\r\n   * @param count Number of members to retrieve\r\n   * @returns Group members list\r\n   */\r\n  async getGroupMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    offset: number = 0,\r\n    count: number = 50\r\n  ): Promise<{ members: GroupMember[]; total: number }> {\r\n    try {\r\n      const response = await this.client.apiGet<{\r\n        members: GroupMember[];\r\n        total: number;\r\n      }>(this.endpoints.group.getMembers, accessToken, {\r\n        group_id: groupId,\r\n        offset,\r\n        count,\r\n      });\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupMessageError(error, \"Failed to get group members\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi danh sách tin nhắn tới 1 group với delay tùy chỉnh\r\n   * Hỗ trợ tất cả các loại tin nhắn: text, image, file, sticker, mention\r\n   *\r\n   * @param request Thông tin request gửi danh sách tin nhắn\r\n   * @returns Kết quả gửi từng tin nhắn\r\n   */\r\n  public async sendMessageListToGroup(\r\n    request: SendMessageListToGroupRequest\r\n  ): Promise<SendMessageListToGroupResponse> {\r\n    const startTime = Date.now();\r\n    const messageResults: SendMessageListToGroupResponse[\"messageResults\"] = [];\r\n    let successfulMessages = 0;\r\n    let failedMessages = 0;\r\n\r\n    try {\r\n      // Validate input\r\n      if (!request.accessToken || request.accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.groupId || request.groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.messages || request.messages.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách tin nhắn không được để trống\", -1);\r\n      }\r\n\r\n      // Gửi từng tin nhắn theo thứ tự\r\n      for (let i = 0; i < request.messages.length; i++) {\r\n        const message = request.messages[i];\r\n        const messageStartTime = Date.now();\r\n\r\n        // Callback: Bắt đầu gửi tin nhắn\r\n        if (request.onProgress) {\r\n          request.onProgress({\r\n            groupId: request.groupId,\r\n            messageIndex: i,\r\n            totalMessages: request.messages.length,\r\n            messageType: message.type,\r\n            status: 'started',\r\n            startTime: messageStartTime,\r\n          });\r\n        }\r\n\r\n        const messageResult = {\r\n          messageIndex: i,\r\n          messageType: message.type,\r\n          success: false,\r\n          startTime: messageStartTime,\r\n          endTime: 0,\r\n          duration: 0,\r\n        } as SendMessageListToGroupResponse[\"messageResults\"][0];\r\n\r\n        try {\r\n          let result: GroupMessageResult;\r\n\r\n          // Gửi tin nhắn theo loại\r\n          switch (message.type) {\r\n            case \"text\":\r\n              if (!message.text) {\r\n                throw new Error(\"Text không được để trống cho text message\");\r\n              }\r\n              result = await this.sendTextMessage(\r\n                request.accessToken,\r\n                request.groupId,\r\n                { type: \"text\", text: message.text }\r\n              );\r\n              break;\r\n\r\n            case \"image\":\r\n              if (!message.imageUrl && !message.attachmentId) {\r\n                throw new Error(\"imageUrl hoặc attachmentId là bắt buộc cho image message\");\r\n              }\r\n              result = await this.sendImageMessage(\r\n                request.accessToken,\r\n                request.groupId,\r\n                {\r\n                  type: \"image\",\r\n                  imageUrl: message.imageUrl,\r\n                  attachmentId: message.attachmentId,\r\n                  caption: message.caption,\r\n                }\r\n              );\r\n              break;\r\n\r\n            case \"file\":\r\n              if (!message.fileToken) {\r\n                throw new Error(\"fileToken là bắt buộc cho file message\");\r\n              }\r\n              result = await this.sendFileMessage(\r\n                request.accessToken,\r\n                request.groupId,\r\n                {\r\n                  type: \"file\",\r\n                  fileToken: message.fileToken,\r\n                  fileName: message.fileName,\r\n                }\r\n              );\r\n              break;\r\n\r\n            case \"sticker\":\r\n              if (!message.stickerId) {\r\n                throw new Error(\"stickerId là bắt buộc cho sticker message\");\r\n              }\r\n              result = await this.sendStickerMessage(\r\n                request.accessToken,\r\n                request.groupId,\r\n                {\r\n                  type: \"sticker\",\r\n                  stickerId: message.stickerId,\r\n                }\r\n              );\r\n              break;\r\n\r\n            case \"mention\":\r\n              if (!message.text || !message.mentions) {\r\n                throw new Error(\"text và mentions là bắt buộc cho mention message\");\r\n              }\r\n              result = await this.sendMentionMessage(\r\n                request.accessToken,\r\n                request.groupId,\r\n                {\r\n                  type: \"mention\",\r\n                  text: message.text,\r\n                  mentions: message.mentions,\r\n                }\r\n              );\r\n              break;\r\n\r\n            default:\r\n              throw new Error(`Loại tin nhắn không được hỗ trợ: ${message.type}`);\r\n          }\r\n\r\n          // Ghi nhận thành công\r\n          const messageEndTime = Date.now();\r\n          messageResult.success = true;\r\n          messageResult.result = result;\r\n          messageResult.endTime = messageEndTime;\r\n          messageResult.duration = messageEndTime - messageStartTime;\r\n\r\n          successfulMessages++;\r\n\r\n          // Callback: Hoàn thành thành công\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              groupId: request.groupId,\r\n              messageIndex: i,\r\n              totalMessages: request.messages.length,\r\n              messageType: message.type,\r\n              status: 'completed',\r\n              result: result,\r\n              startTime: messageStartTime,\r\n              endTime: messageEndTime,\r\n            });\r\n          }\r\n\r\n        } catch (error) {\r\n          // Ghi nhận thất bại\r\n          const messageEndTime = Date.now();\r\n          const errorMessage = error instanceof Error ? error.message : String(error);\r\n\r\n          messageResult.success = false;\r\n          messageResult.error = errorMessage;\r\n          messageResult.endTime = messageEndTime;\r\n          messageResult.duration = messageEndTime - messageStartTime;\r\n\r\n          failedMessages++;\r\n\r\n          // Callback: Thất bại\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              groupId: request.groupId,\r\n              messageIndex: i,\r\n              totalMessages: request.messages.length,\r\n              messageType: message.type,\r\n              status: 'failed',\r\n              error: errorMessage,\r\n              startTime: messageStartTime,\r\n              endTime: messageEndTime,\r\n            });\r\n          }\r\n        }\r\n\r\n        messageResults.push(messageResult);\r\n\r\n        // Delay trước khi gửi tin nhắn tiếp theo (trừ tin nhắn cuối cùng)\r\n        if (i < request.messages.length - 1) {\r\n          const delayTime = message.delay ?? request.defaultDelay ?? 0;\r\n          if (delayTime > 0) {\r\n            await this.sleep(delayTime);\r\n          }\r\n        }\r\n      }\r\n\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      return {\r\n        groupId: request.groupId,\r\n        totalMessages: request.messages.length,\r\n        successfulMessages,\r\n        failedMessages,\r\n        messageResults,\r\n        totalDuration,\r\n      };\r\n\r\n    } catch (error) {\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n\r\n      throw new ZaloSDKError(\r\n        `Failed to send message list to group: ${(error as Error).message}`,\r\n        -1,\r\n        {\r\n          groupId: request.groupId,\r\n          totalMessages: request.messages?.length || 0,\r\n          successfulMessages,\r\n          failedMessages,\r\n          messageResults,\r\n          totalDuration,\r\n        }\r\n      );\r\n    }\r\n  }\r\n\r\n\r\n  private handleGroupMessageError(error: unknown, defaultMessage: string): Error {\r\n    let errorDetails = '';\r\n    \r\n    // Type guard for axios error with response\r\n    if (\r\n      error &&\r\n      typeof error === 'object' &&\r\n      'response' in error &&\r\n      error.response &&\r\n      typeof error.response === 'object' &&\r\n      'data' in error.response\r\n    ) {\r\n      const errorData = error.response.data as Record<string, unknown>;\r\n      const errorCode = errorData.error || (error.response as { status?: number }).status;\r\n      const errorMessage = errorData.message || errorData.error_description || \"Unknown error\";\r\n      \r\n      errorDetails = `Error ${errorCode}: ${errorMessage}`;\r\n      \r\n      // Add additional debug info if available\r\n      if (errorData.error_name) {\r\n        errorDetails += ` (${errorData.error_name})`;\r\n      }\r\n      \r\n      // Log detailed error for debugging\r\n      console.error(`[GroupMessageService] ${defaultMessage}`, {\r\n        errorCode,\r\n        errorMessage,\r\n        errorData,\r\n        url: (error as { config?: { url?: string } }).config?.url,\r\n        method: (error as { config?: { method?: string } }).config?.method,\r\n        requestData: (error as { config?: { data?: unknown } }).config?.data\r\n      });\r\n      \r\n      return new Error(`${defaultMessage}: ${errorDetails}`);\r\n    }\r\n    \r\n    // Handle network or other errors\r\n    const errorObj = error as Error;\r\n    console.error(`[GroupMessageService] ${defaultMessage}`, {\r\n      message: errorObj.message,\r\n      stack: errorObj.stack,\r\n      url: (error as { config?: { url?: string } }).config?.url,\r\n      method: (error as { config?: { method?: string } }).config?.method,\r\n    });\r\n    \r\n    return new Error(`${defaultMessage}: ${errorObj.message || \"Unknown error\"}`);\r\n  }\r\n\r\n  /**\r\n   * Gửi danh sách tin nhắn tới nhiều groups với callback tracking\r\n   *\r\n   * @param request Thông tin request gửi danh sách tin nhắn tới nhiều groups\r\n   * @returns Kết quả gửi tin nhắn cho tất cả groups\r\n   */\r\n  public async sendMessageListToMultipleGroups(\r\n    request: SendMessageListToMultipleGroupsRequest\r\n  ): Promise<SendMessageListToMultipleGroupsResponse> {\r\n    const startTime = Date.now();\r\n    const groupResults: SendMessageListToMultipleGroupsResponse[\"groupResults\"] = [];\r\n    let successfulGroups = 0;\r\n    let failedGroups = 0;\r\n    let totalSuccessfulMessages = 0;\r\n    let totalFailedMessages = 0;\r\n\r\n    try {\r\n      // Validate input\r\n      if (!request.accessToken || request.accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.groupIds || request.groupIds.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách group IDs không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.messages || request.messages.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách tin nhắn không được để trống\", -1);\r\n      }\r\n\r\n      // Loại bỏ group IDs trùng lặp và rỗng\r\n      const uniqueGroupIds = [...new Set(request.groupIds.filter(id => id && id.trim().length > 0))];\r\n\r\n      if (uniqueGroupIds.length === 0) {\r\n        throw new ZaloSDKError(\"Không có group ID hợp lệ nào\", -1);\r\n      }\r\n\r\n      // Gửi tin nhắn cho từng group tuần tự\r\n      for (let i = 0; i < uniqueGroupIds.length; i++) {\r\n        const groupId = uniqueGroupIds[i];\r\n        const groupStartTime = Date.now();\r\n\r\n        // Callback: Bắt đầu gửi cho group\r\n        if (request.onProgress) {\r\n          request.onProgress({\r\n            groupId,\r\n            groupIndex: i,\r\n            totalGroups: uniqueGroupIds.length,\r\n            status: 'started',\r\n            startTime: groupStartTime,\r\n          });\r\n        }\r\n\r\n        const groupResult = {\r\n          groupId,\r\n          groupIndex: i,\r\n          success: false,\r\n          startTime: groupStartTime,\r\n          endTime: 0,\r\n          duration: 0,\r\n        } as SendMessageListToMultipleGroupsResponse[\"groupResults\"][0];\r\n\r\n        try {\r\n          // Gửi danh sách tin nhắn cho group này\r\n          const messageListResult = await this.sendMessageListToGroup({\r\n            accessToken: request.accessToken,\r\n            groupId,\r\n            messages: request.messages,\r\n            defaultDelay: request.defaultDelay,\r\n            // Không truyền onProgress để tránh callback lồng nhau\r\n          });\r\n\r\n          // Ghi nhận thành công\r\n          const groupEndTime = Date.now();\r\n          groupResult.success = true;\r\n          groupResult.messageListResult = messageListResult;\r\n          groupResult.endTime = groupEndTime;\r\n          groupResult.duration = groupEndTime - groupStartTime;\r\n\r\n          successfulGroups++;\r\n          totalSuccessfulMessages += messageListResult.successfulMessages;\r\n          totalFailedMessages += messageListResult.failedMessages;\r\n\r\n          // Callback: Hoàn thành thành công\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              groupId,\r\n              groupIndex: i,\r\n              totalGroups: uniqueGroupIds.length,\r\n              status: 'completed',\r\n              result: messageListResult,\r\n              startTime: groupStartTime,\r\n              endTime: groupEndTime,\r\n            });\r\n          }\r\n\r\n        } catch (error) {\r\n          // Ghi nhận thất bại\r\n          const groupEndTime = Date.now();\r\n          const errorMessage = error instanceof Error ? error.message : String(error);\r\n\r\n          groupResult.success = false;\r\n          groupResult.error = errorMessage;\r\n          groupResult.endTime = groupEndTime;\r\n          groupResult.duration = groupEndTime - groupStartTime;\r\n\r\n          failedGroups++;\r\n          // Với group thất bại, coi như tất cả tin nhắn đều thất bại\r\n          totalFailedMessages += request.messages.length;\r\n\r\n          // Callback: Thất bại\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              groupId,\r\n              groupIndex: i,\r\n              totalGroups: uniqueGroupIds.length,\r\n              status: 'failed',\r\n              error: errorMessage,\r\n              startTime: groupStartTime,\r\n              endTime: groupEndTime,\r\n            });\r\n          }\r\n        }\r\n\r\n        groupResults.push(groupResult);\r\n\r\n        // Delay giữa các groups (trừ group cuối cùng)\r\n        if (i < uniqueGroupIds.length - 1 && request.delayBetweenGroups && request.delayBetweenGroups > 0) {\r\n          await this.sleep(request.delayBetweenGroups);\r\n        }\r\n      }\r\n\r\n      const totalDuration = Date.now() - startTime;\r\n      const totalMessages = uniqueGroupIds.length * request.messages.length;\r\n\r\n      return {\r\n        totalGroups: uniqueGroupIds.length,\r\n        successfulGroups,\r\n        failedGroups,\r\n        groupResults,\r\n        totalDuration,\r\n        messageStats: {\r\n          totalSuccessfulMessages,\r\n          totalFailedMessages,\r\n          totalMessages,\r\n        },\r\n      };\r\n\r\n    } catch (error) {\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n\r\n      throw new ZaloSDKError(\r\n        `Failed to send message list to multiple groups: ${(error as Error).message}`,\r\n        -1,\r\n        {\r\n          totalGroups: request.groupIds?.length || 0,\r\n          successfulGroups,\r\n          failedGroups,\r\n          groupResults,\r\n          totalDuration,\r\n          messageStats: {\r\n            totalSuccessfulMessages,\r\n            totalFailedMessages,\r\n            totalMessages: (request.groupIds?.length || 0) * (request.messages?.length || 0),\r\n          },\r\n        }\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn cá nhân hóa tới nhiều groups - mỗi group có bộ tin nhắn riêng\r\n   *\r\n   * @param request Thông tin request gửi tin nhắn cá nhân hóa tới nhiều groups\r\n   * @returns Kết quả gửi tin nhắn cho tất cả groups\r\n   */\r\n  public async sendPersonalizedMessageToMultipleGroups(\r\n    request: SendPersonalizedMessageToMultipleGroupsRequest\r\n  ): Promise<SendPersonalizedMessageToMultipleGroupsResponse> {\r\n    const startTime = Date.now();\r\n    const groupResults: SendPersonalizedMessageToMultipleGroupsResponse[\"groupResults\"] = [];\r\n    let successfulGroups = 0;\r\n    let failedGroups = 0;\r\n    let totalSuccessfulMessages = 0;\r\n    let totalFailedMessages = 0;\r\n    let totalMessages = 0;\r\n\r\n    try {\r\n      // Validate input\r\n      if (!request.accessToken || request.accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.personalizedMessages || request.personalizedMessages.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách tin nhắn cá nhân hóa không được để trống\", -1);\r\n      }\r\n\r\n      // Validate từng group message\r\n      for (let i = 0; i < request.personalizedMessages.length; i++) {\r\n        const personalizedMessage = request.personalizedMessages[i];\r\n\r\n        if (!personalizedMessage.groupId || personalizedMessage.groupId.trim().length === 0) {\r\n          throw new ZaloSDKError(`Group ID tại index ${i} không được để trống`, -1);\r\n        }\r\n\r\n        if (!personalizedMessage.messages || personalizedMessage.messages.length === 0) {\r\n          throw new ZaloSDKError(`Danh sách tin nhắn cho group ${personalizedMessage.groupId} không được để trống`, -1);\r\n        }\r\n      }\r\n\r\n      // Loại bỏ groups trùng lặp dựa trên groupId\r\n      const uniquePersonalizedMessages = request.personalizedMessages.filter(\r\n        (message, index, self) =>\r\n          self.findIndex(m => m.groupId === message.groupId) === index &&\r\n          message.groupId.trim().length > 0\r\n      );\r\n\r\n      if (uniquePersonalizedMessages.length === 0) {\r\n        throw new ZaloSDKError(\"Không có group message hợp lệ nào\", -1);\r\n      }\r\n\r\n      // Tính tổng số tin nhắn\r\n      totalMessages = uniquePersonalizedMessages.reduce((sum, pm) => sum + pm.messages.length, 0);\r\n\r\n      // Gửi tin nhắn cho từng group tuần tự\r\n      for (let i = 0; i < uniquePersonalizedMessages.length; i++) {\r\n        const personalizedMessage = uniquePersonalizedMessages[i];\r\n        const groupStartTime = Date.now();\r\n\r\n        // Callback: Bắt đầu gửi cho group\r\n        if (request.onProgress) {\r\n          request.onProgress({\r\n            groupId: personalizedMessage.groupId,\r\n            groupIndex: i,\r\n            totalGroups: uniquePersonalizedMessages.length,\r\n            status: 'started',\r\n            startTime: groupStartTime,\r\n          });\r\n        }\r\n\r\n        const groupResult = {\r\n          groupId: personalizedMessage.groupId,\r\n          groupIndex: i,\r\n          success: false,\r\n          startTime: groupStartTime,\r\n          endTime: 0,\r\n          duration: 0,\r\n        } as SendPersonalizedMessageToMultipleGroupsResponse[\"groupResults\"][0];\r\n\r\n        try {\r\n          // Gửi danh sách tin nhắn cá nhân hóa cho group này\r\n          const messageListResult = await this.sendMessageListToGroup({\r\n            accessToken: request.accessToken,\r\n            groupId: personalizedMessage.groupId,\r\n            messages: personalizedMessage.messages,\r\n            defaultDelay: request.defaultDelay,\r\n            // Không truyền onProgress để tránh callback lồng nhau\r\n          });\r\n\r\n          // Ghi nhận thành công\r\n          const groupEndTime = Date.now();\r\n          groupResult.success = true;\r\n          groupResult.messageListResult = messageListResult;\r\n          groupResult.endTime = groupEndTime;\r\n          groupResult.duration = groupEndTime - groupStartTime;\r\n\r\n          successfulGroups++;\r\n          totalSuccessfulMessages += messageListResult.successfulMessages;\r\n          totalFailedMessages += messageListResult.failedMessages;\r\n\r\n          // Callback: Hoàn thành thành công\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              groupId: personalizedMessage.groupId,\r\n              groupIndex: i,\r\n              totalGroups: uniquePersonalizedMessages.length,\r\n              status: 'completed',\r\n              result: messageListResult,\r\n              startTime: groupStartTime,\r\n              endTime: groupEndTime,\r\n            });\r\n          }\r\n\r\n        } catch (error) {\r\n          // Ghi nhận thất bại\r\n          const groupEndTime = Date.now();\r\n          const errorMessage = error instanceof Error ? error.message : String(error);\r\n\r\n          groupResult.success = false;\r\n          groupResult.error = errorMessage;\r\n          groupResult.endTime = groupEndTime;\r\n          groupResult.duration = groupEndTime - groupStartTime;\r\n\r\n          failedGroups++;\r\n          // Với group thất bại, coi như tất cả tin nhắn của group đó đều thất bại\r\n          totalFailedMessages += personalizedMessage.messages.length;\r\n\r\n          // Callback: Thất bại\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              groupId: personalizedMessage.groupId,\r\n              groupIndex: i,\r\n              totalGroups: uniquePersonalizedMessages.length,\r\n              status: 'failed',\r\n              error: errorMessage,\r\n              startTime: groupStartTime,\r\n              endTime: groupEndTime,\r\n            });\r\n          }\r\n        }\r\n\r\n        groupResults.push(groupResult);\r\n\r\n        // Delay giữa các groups (trừ group cuối cùng)\r\n        if (i < uniquePersonalizedMessages.length - 1 && request.delayBetweenGroups && request.delayBetweenGroups > 0) {\r\n          await this.sleep(request.delayBetweenGroups);\r\n        }\r\n      }\r\n\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      return {\r\n        totalGroups: uniquePersonalizedMessages.length,\r\n        successfulGroups,\r\n        failedGroups,\r\n        groupResults,\r\n        totalDuration,\r\n        messageStats: {\r\n          totalSuccessfulMessages,\r\n          totalFailedMessages,\r\n          totalMessages,\r\n        },\r\n      };\r\n\r\n    } catch (error) {\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n\r\n      throw new ZaloSDKError(\r\n        `Failed to send personalized messages to multiple groups: ${(error as Error).message}`,\r\n        -1,\r\n        {\r\n          totalGroups: request.personalizedMessages?.length || 0,\r\n          successfulGroups,\r\n          failedGroups,\r\n          groupResults,\r\n          totalDuration,\r\n          messageStats: {\r\n            totalSuccessfulMessages,\r\n            totalFailedMessages,\r\n            totalMessages,\r\n          },\r\n        }\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Utility method để sleep/delay\r\n   * @param ms Thời gian delay tính bằng milliseconds\r\n   */\r\n  private sleep(ms: number): Promise<void> {\r\n    return new Promise(resolve => setTimeout(resolve, ms));\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  GroupCreateRequest,\r\n  GroupCreateResult,\r\n  GroupCreateData,\r\n  GroupUpdateRequest,\r\n  GroupUpdateResult,\r\n  GroupAssetUpdateRequest,\r\n  GroupAvatarUpdateRequest,\r\n  GroupMemberInviteRequest,\r\n  GroupInviteResult,\r\n  GroupMemberActionRequest,\r\n  GroupAdminActionRequest,\r\n  GroupDeleteRequest,\r\n  RecentChatsResponse,\r\n  GroupConversationResponse,\r\n  GroupsOfOAResponse,\r\n  GroupDetailResponse,\r\n  GroupPendingMembersResponse,\r\n  GroupAcceptPendingMembersRequest,\r\n  GroupAcceptPendingMembersResponse,\r\n  GroupRemoveMembersRequest,\r\n  GroupRemoveMembersResponse,\r\n  GroupMembersResponse,\r\n  GroupMemberInfo,\r\n  GroupQuotaMessageRequest,\r\n  GroupQuotaMessageResponse,\r\n  AllGroupMembersResponse,\r\n  GetAllMembersProgress,\r\n  EnhancedGroupMember,\r\n  AllGroupMembersWithDetailsResponse,\r\n  GetAllMembersWithDetailsProgress,\r\n  OAGroupItem,\r\n  AllGroupsOfOAResponse,\r\n} from \"../types/group\";\r\nimport { GMFProductType, QuotaType } from \"../types/oa\";\r\nimport { ZaloSDKError } from \"../types/common\";\r\nimport { UserService } from \"./user.service\";\r\nimport { UserInfo } from \"../types/user\";\r\n\r\n/**\r\n * Service for handling Zalo Official Account Group Management Framework (GMF) APIs\r\n *\r\n * CONDITIONS FOR USING ZALO GMF GROUP MANAGEMENT:\r\n *\r\n * 1. GENERAL CONDITIONS:\r\n *    - OA must be granted permission to use GMF (Group Message Framework) feature\r\n *    - Access token must have \"manage_group\" and \"group_message\" scopes\r\n *    - OA must have active status and be verified\r\n *    - Must comply with limits on number of groups and members\r\n *\r\n * 2. CREATE NEW GROUP:\r\n *    - Group name: required, max 100 characters, no special characters\r\n *    - Description: optional, max 500 characters\r\n *    - Avatar: optional, JPG/PNG format, max 5MB\r\n *    - Initial members: max 200 people, must be users who have interacted with OA\r\n *    - OA automatically becomes admin of the group\r\n *\r\n * 3. MEMBER MANAGEMENT:\r\n *    - Only admins can invite/remove members\r\n *    - Invite members: max 50 people per time, users must have interacted with OA\r\n *    - Remove members: cannot remove other admins, must have at least 1 admin\r\n *    - Members can leave group themselves\r\n *\r\n * 4. ADMIN MANAGEMENT:\r\n *    - Only current admins can add/remove other admins\r\n *    - Must have at least 1 admin in group\r\n *    - OA always has admin rights and cannot be removed\r\n *\r\n * 5. LIMITS AND CONSTRAINTS:\r\n *    - Maximum groups: according to service package (usually 10-100 groups)\r\n *    - Maximum members per group: 200 people\r\n *    - Group creation frequency: max 10 groups/day\r\n *    - Member invitation frequency: max 500 invitations/day\r\n */\r\nexport class GroupManagementService {\r\n\r\n\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    group: {\r\n      create: \"https://openapi.zalo.me/v3.0/oa/group/creategroupwithoa\",\r\n      get: \"https://openapi.zalo.me/v3.0/oa/group/getgroup\",\r\n      updateInfo: \"https://openapi.zalo.me/v3.0/oa/group/updateinfo\",\r\n      updateAsset: \"https://openapi.zalo.me/v3.0/oa/group/updateasset\",\r\n      invite: \"https://openapi.zalo.me/v3.0/oa/group/invite\",\r\n      listPendingInvite: \"https://openapi.zalo.me/v3.0/oa/group/listpendinginvite\",\r\n      acceptPendingInvite: \"https://openapi.zalo.me/v3.0/oa/group/acceptpendinginvite\",\r\n      rejectPendingInvite: \"https://openapi.zalo.me/v3.0/oa/group/rejectpendinginvite\",\r\n      removeMembers: \"https://openapi.zalo.me/v3.0/oa/group/removemembers\",\r\n      getGroupsOfOA: \"https://openapi.zalo.me/v3.0/oa/group/getgroupsofoa\",\r\n      recent: \"https://openapi.zalo.me/v3.0/oa/group/listrecentchat\",\r\n      conversation: \"https://openapi.zalo.me/v3.0/oa/group/conversation\",\r\n      addAdmins: \"https://openapi.zalo.me/v3.0/oa/group/addadmins\",\r\n      removeAdmins: \"https://openapi.zalo.me/v3.0/oa/group/removeadmins\",\r\n      conversationByGroupId: (groupId: string) => `https://openapi.zalo.me/v3.0/oa/group/${groupId}/conversation`,\r\n      members: \"https://openapi.zalo.me/v3.0/oa/group/listmember\",\r\n      delete: \"https://openapi.zalo.me/v3.0/oa/group/delete\",\r\n      quota: \"https://openapi.zalo.me/v3.0/oa/quota/group\",\r\n    },\r\n  } as const;\r\n\r\n  constructor(\r\n    private readonly client: ZaloClient,\r\n    private readonly userService?: UserService\r\n  ) { }\r\n\r\n  /**\r\n   * Create GroupManagementService with UserService for enhanced member details\r\n   * @param client ZaloClient instance\r\n   * @param userService UserService instance for fetching detailed user info\r\n   * @returns GroupManagementService with enhanced capabilities\r\n   */\r\n  static withUserService(client: ZaloClient, userService: UserService): GroupManagementService {\r\n    return new GroupManagementService(client, userService);\r\n  }\r\n\r\n  /**\r\n   * Create basic GroupManagementService without UserService\r\n   * @param client ZaloClient instance\r\n   * @returns GroupManagementService with basic capabilities only\r\n   */\r\n  static basic(client: ZaloClient): GroupManagementService {\r\n    return new GroupManagementService(client);\r\n  }\r\n\r\n  /**\r\n   * Create new group chat with asset_id\r\n   * @param accessToken OA access token\r\n   * @param groupData Group information to create\r\n   * @returns Created group information\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/creategroupwithoa\r\n   */\r\n  async createGroup(\r\n    accessToken: string,\r\n    groupData: GroupCreateRequest\r\n  ): Promise<GroupCreateResult> {\r\n    try {\r\n      // Validate input\r\n      if (!groupData.group_name || groupData.group_name.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group name cannot be empty\", -1);\r\n      }\r\n\r\n      if (groupData.group_name.length > 100) {\r\n        throw new ZaloSDKError(\"Group name cannot exceed 100 characters\", -1);\r\n      }\r\n\r\n      if (!groupData.asset_id || groupData.asset_id.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Asset ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (groupData.member_user_ids.length > 99) {\r\n        throw new ZaloSDKError(\r\n          \"Initial member count cannot exceed 99 people\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      if (groupData.member_user_ids.length === 0) {\r\n        throw new ZaloSDKError(\"Member list cannot be empty\", -1);\r\n      }\r\n\r\n      const requestData = {\r\n        group_name: groupData.group_name.trim(),\r\n        ...(groupData.group_description && {\r\n          group_description: groupData.group_description.trim(),\r\n        }),\r\n        asset_id: groupData.asset_id,\r\n        member_user_ids: groupData.member_user_ids,\r\n      };\r\n\r\n      const response = await this.client.apiPost<GroupCreateResult>(\r\n        this.endpoints.group.create,\r\n        accessToken,\r\n        requestData\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(error, \"Failed to create group\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Helper method to extract group data from create response\r\n   * @param response Full API response\r\n   * @returns Group data only\r\n   */\r\n  extractGroupData(response: GroupCreateResult): GroupCreateData {\r\n    return response.data;\r\n  }\r\n\r\n  /**\r\n   * Helper method to extract group info from update response\r\n   * @param response Full API response\r\n   * @returns Group info only\r\n   */\r\n  extractGroupInfo(response: GroupUpdateResult) {\r\n    return response.data.group_info;\r\n  }\r\n\r\n  /**\r\n   * Helper method to extract group settings from update response\r\n   * @param response Full API response\r\n   * @returns Group settings only\r\n   */\r\n  extractGroupSettings(response: GroupUpdateResult) {\r\n    return response.data.group_setting;\r\n  }\r\n\r\n  /**\r\n   * Helper method to extract asset info from update response\r\n   * @param response Full API response\r\n   * @returns Asset info only\r\n   */\r\n  extractAssetInfo(response: GroupUpdateResult) {\r\n    return response.data.asset_info;\r\n  }\r\n\r\n  /**\r\n   * Update group asset (upgrade package or extend expiration)\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param assetId New asset ID for the group\r\n   * @returns Update result with full group information\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/updateasset\r\n   *\r\n   * Use cases:\r\n   * - Increase member limit for the group\r\n   * - Extend group expiration when expired\r\n   */\r\n  async updateGroupAsset(\r\n    accessToken: string,\r\n    groupId: string,\r\n    assetId: string\r\n  ): Promise<GroupUpdateResult>;\r\n\r\n  /**\r\n   * Update group asset (upgrade package or extend expiration) - Object parameter version\r\n   * @param accessToken OA access token\r\n   * @param updateData Asset update data\r\n   * @returns Update result with full group information\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/updateasset\r\n   */\r\n  async updateGroupAsset(\r\n    accessToken: string,\r\n    updateData: GroupAssetUpdateRequest\r\n  ): Promise<GroupUpdateResult>;\r\n\r\n  async updateGroupAsset(\r\n    accessToken: string,\r\n    groupIdOrUpdateData: string | GroupAssetUpdateRequest,\r\n    assetId?: string\r\n  ): Promise<GroupUpdateResult> {\r\n    try {\r\n      let requestData: GroupAssetUpdateRequest;\r\n\r\n      // Handle overloaded parameters\r\n      if (typeof groupIdOrUpdateData === 'string') {\r\n        // First overload: (accessToken, groupId, assetId)\r\n        if (!assetId) {\r\n          throw new ZaloSDKError(\"Asset ID is required when using separate parameters\", -1);\r\n        }\r\n\r\n        requestData = {\r\n          group_id: groupIdOrUpdateData,\r\n          asset_id: assetId,\r\n        };\r\n      } else {\r\n        // Second overload: (accessToken, updateData)\r\n        requestData = groupIdOrUpdateData;\r\n      }\r\n\r\n      // Validate input\r\n      if (!requestData.group_id || requestData.group_id.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (!requestData.asset_id || requestData.asset_id.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Asset ID cannot be empty\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiPost<GroupUpdateResult>(\r\n        this.endpoints.group.updateAsset,\r\n        accessToken,\r\n        requestData\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to update group asset\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get detailed group information\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @returns Detailed group information including group_info, asset_info and group_setting\r\n   *\r\n   * API: GET https://openapi.zalo.me/v3.0/oa/group/getgroup\r\n   */\r\n  async getGroupInfo(\r\n    accessToken: string,\r\n    groupId: string\r\n  ): Promise<GroupDetailResponse> {\r\n    try {\r\n      // Validate access token\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      // Validate group ID\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<GroupDetailResponse>(\r\n        this.endpoints.group.get,\r\n        accessToken,\r\n        {\r\n          group_id: groupId.trim(),\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get group information\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Update group information\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param updateData Information to update\r\n   * @returns Update result with full group information\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/updateinfo\r\n   */\r\n  async updateGroupInfo(\r\n    accessToken: string,\r\n    groupId: string,\r\n    updateData: GroupUpdateRequest\r\n  ): Promise<GroupUpdateResult> {\r\n    try {\r\n      // Validate input\r\n      if (updateData.group_name && updateData.group_name.length > 100) {\r\n        throw new ZaloSDKError(\"Group name cannot exceed 100 characters\", -1);\r\n      }\r\n\r\n      if (updateData.group_description && updateData.group_description.length > 500) {\r\n        throw new ZaloSDKError(\r\n          \"Group description cannot exceed 500 characters\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Build request data according to Zalo API\r\n      const requestData = {\r\n        group_id: groupId,\r\n        ...(updateData.group_name && {\r\n          group_name: updateData.group_name.trim(),\r\n        }),\r\n        ...(updateData.group_avatar && {\r\n          group_avatar: updateData.group_avatar,\r\n        }),\r\n        ...(updateData.group_description && {\r\n          group_description: updateData.group_description.trim(),\r\n        }),\r\n        ...(updateData.lock_send_msg !== undefined && {\r\n          lock_send_msg: updateData.lock_send_msg,\r\n        }),\r\n        ...(updateData.join_appr !== undefined && {\r\n          join_appr: updateData.join_appr,\r\n        }),\r\n        ...(updateData.enable_msg_history !== undefined && {\r\n          enable_msg_history: updateData.enable_msg_history,\r\n        }),\r\n        ...(updateData.enable_link_join !== undefined && {\r\n          enable_link_join: updateData.enable_link_join,\r\n        }),\r\n      };\r\n\r\n      const response = await this.client.apiPost<GroupUpdateResult>(\r\n        this.endpoints.group.updateInfo,\r\n        accessToken,\r\n        requestData\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to update group information\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Update group avatar\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param avatarData New avatar information\r\n   * @returns Update result\r\n   * @deprecated Use updateGroupInfo() with group_avatar field instead\r\n   */\r\n  async updateGroupAvatar(\r\n    accessToken: string,\r\n    groupId: string,\r\n    avatarData: GroupAvatarUpdateRequest\r\n  ): Promise<{ success: boolean }> {\r\n    try {\r\n      const response = await this.client.apiPost<{ error: number; message: string }>(\r\n        this.endpoints.group.updateInfo,\r\n        accessToken,\r\n        {\r\n          group_id: groupId,\r\n          ...avatarData,\r\n        }\r\n      );\r\n\r\n      return { success: response.error === 0 };\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to update group avatar\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Invite members to group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param inviteData List of user IDs to invite\r\n   * @returns Invitation result\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/invite\r\n   */\r\n  async inviteMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    inviteData: GroupMemberInviteRequest\r\n  ): Promise<GroupInviteResult>;\r\n\r\n  /**\r\n   * Invite members to group - Array parameter version\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param memberUserIds Array of user IDs to invite\r\n   * @returns Invitation result\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/invite\r\n   */\r\n  async inviteMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    memberUserIds: string[]\r\n  ): Promise<GroupInviteResult>;\r\n\r\n  async inviteMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    inviteDataOrUserIds: GroupMemberInviteRequest | string[]\r\n  ): Promise<GroupInviteResult> {\r\n    try {\r\n      let memberUserIds: string[];\r\n\r\n      // Handle overloaded parameters\r\n      if (Array.isArray(inviteDataOrUserIds)) {\r\n        // Second overload: (accessToken, groupId, memberUserIds)\r\n        memberUserIds = inviteDataOrUserIds;\r\n      } else {\r\n        // First overload: (accessToken, groupId, inviteData)\r\n        memberUserIds = inviteDataOrUserIds.member_user_ids;\r\n      }\r\n\r\n      // Validate input\r\n      if (memberUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"Member list cannot be empty\", -1);\r\n      }\r\n\r\n      if (memberUserIds.length > 50) {\r\n        throw new ZaloSDKError(\"Cannot invite more than 50 people at once\", -1);\r\n      }\r\n\r\n      const requestData = {\r\n        group_id: groupId,\r\n        member_user_ids: memberUserIds,\r\n      };\r\n\r\n      const response = await this.client.apiPost<GroupInviteResult>(\r\n        this.endpoints.group.invite,\r\n        accessToken,\r\n        requestData\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(error, \"Failed to invite members\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get list of pending members\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param offset Offset for pagination (default: 0)\r\n   * @param count Maximum number to return (default: 20, max: 50)\r\n   * @returns List of pending members\r\n   */\r\n  async getPendingMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    offset: number = 0,\r\n    count: number = 5\r\n  ): Promise<GroupPendingMembersResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (offset < 0) {\r\n        throw new ZaloSDKError(\"Offset must be >= 0\", -1);\r\n      }\r\n\r\n      if (count <= 0 || count > 50) {\r\n        throw new ZaloSDKError(\"Count must be from 1 to 50\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<GroupPendingMembersResponse>(\r\n        this.endpoints.group.listPendingInvite,\r\n        accessToken,\r\n        {\r\n          group_id: groupId.trim(),\r\n          offset: offset,\r\n          count: count,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get pending members\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Accept pending members to group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param memberUserIds List of user IDs to accept\r\n   * @returns Accept result\r\n   */\r\n  async acceptPendingMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    memberUserIds: string[]\r\n  ): Promise<GroupAcceptPendingMembersResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (!memberUserIds || memberUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"Member user IDs list cannot be empty\", -1);\r\n      }\r\n\r\n      if (memberUserIds.length > 100) {\r\n        throw new ZaloSDKError(\r\n          \"Cannot accept more than 100 members at once\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Validate user IDs\r\n      const validUserIds = memberUserIds.filter(\r\n        (id) => id && id.trim().length > 0\r\n      );\r\n      if (validUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"No valid user IDs found\", -1);\r\n      }\r\n\r\n      const requestBody: GroupAcceptPendingMembersRequest = {\r\n        group_id: groupId.trim(),\r\n        member_user_ids: validUserIds.map((id) => id.trim()),\r\n      };\r\n\r\n      const response =\r\n        await this.client.apiPost<GroupAcceptPendingMembersResponse>(\r\n          this.endpoints.group.acceptPendingInvite,\r\n          accessToken,\r\n          requestBody\r\n        );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to accept pending members\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Reject pending members from group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param memberUserIds List of user IDs to reject\r\n   * @returns Reject result\r\n   */\r\n  async rejectPendingMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    memberUserIds: string[]\r\n  ): Promise<GroupAcceptPendingMembersResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (!memberUserIds || memberUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"Member user IDs list cannot be empty\", -1);\r\n      }\r\n\r\n      if (memberUserIds.length > 100) {\r\n        throw new ZaloSDKError(\r\n          \"Cannot reject more than 100 members at once\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Validate user IDs\r\n      const validUserIds = memberUserIds.filter(\r\n        (id) => id && id.trim().length > 0\r\n      );\r\n      if (validUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"No valid user IDs found\", -1);\r\n      }\r\n\r\n      const requestBody: GroupAcceptPendingMembersRequest = {\r\n        group_id: groupId.trim(),\r\n        member_user_ids: validUserIds.map((id) => id.trim()),\r\n      };\r\n\r\n      const response =\r\n        await this.client.apiPost<GroupAcceptPendingMembersResponse>(\r\n          this.endpoints.group.rejectPendingInvite,\r\n          accessToken,\r\n          requestBody\r\n        );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to reject pending members\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Remove members from group\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param memberUserIds List of user IDs to remove\r\n   * @returns Remove result\r\n   */\r\n  async removeMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    memberUserIds: string[]\r\n  ): Promise<GroupRemoveMembersResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (!memberUserIds || memberUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"Member user IDs list cannot be empty\", -1);\r\n      }\r\n\r\n      if (memberUserIds.length > 100) {\r\n        throw new ZaloSDKError(\r\n          \"Cannot remove more than 100 members at once\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Validate user IDs\r\n      const validUserIds = memberUserIds.filter(\r\n        (id) => id && id.trim().length > 0\r\n      );\r\n      if (validUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"No valid user IDs found\", -1);\r\n      }\r\n\r\n      const requestBody: GroupRemoveMembersRequest = {\r\n        group_id: groupId.trim(),\r\n        member_user_ids: validUserIds.map((id) => id.trim()),\r\n      };\r\n\r\n      const response = await this.client.apiPost<GroupRemoveMembersResponse>(\r\n        this.endpoints.group.removeMembers,\r\n        accessToken,\r\n        requestBody\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(error, \"Failed to remove members\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Add admin rights to members - Array parameter version\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param memberUserIds Array of user IDs to add admin rights\r\n   * @returns Add admin result\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/addadmins\r\n   */\r\n  async addAdmins(\r\n    accessToken: string,\r\n    groupId: string,\r\n    memberUserIds: string[]\r\n  ): Promise<{ error: number; message: string }>;\r\n\r\n  async addAdmins(\r\n    accessToken: string,\r\n    groupId: string,\r\n    adminDataOrUserIds: GroupAdminActionRequest | string[]\r\n  ): Promise<{ error: number; message: string }> {\r\n    try {\r\n      let memberUserIds: string[];\r\n\r\n      // Handle overloaded parameters\r\n      if (Array.isArray(adminDataOrUserIds)) {\r\n        // Second overload: (accessToken, groupId, memberUserIds)\r\n        memberUserIds = adminDataOrUserIds;\r\n      } else {\r\n        // First overload: (accessToken, groupId, adminData)\r\n        memberUserIds = adminDataOrUserIds.member_user_ids;\r\n      }\r\n\r\n      // Validate input\r\n      if (!memberUserIds || memberUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"Member user IDs list cannot be empty\", -1);\r\n      }\r\n\r\n      const requestData = {\r\n        group_id: groupId,\r\n        member_user_ids: memberUserIds,\r\n      };\r\n\r\n      const response = await this.client.apiPost<{\r\n        error: number;\r\n        message: string;\r\n      }>(this.endpoints.group.addAdmins, accessToken, requestData);\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(error, \"Failed to add admins\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Remove admin rights from members - Array parameter version\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param memberUserIds Array of user IDs to remove admin rights\r\n   * @returns Remove admin result\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/group/removeadmins\r\n   */\r\n  async removeAdmins(\r\n    accessToken: string,\r\n    groupId: string,\r\n    memberUserIds: string[]\r\n  ): Promise<{ error: number; message: string }>;\r\n\r\n  async removeAdmins(\r\n    accessToken: string,\r\n    groupId: string,\r\n    adminDataOrUserIds: GroupAdminActionRequest | string[]\r\n  ): Promise<{ error: number; message: string }> {\r\n    try {\r\n      let memberUserIds: string[];\r\n\r\n      // Handle overloaded parameters\r\n      if (Array.isArray(adminDataOrUserIds)) {\r\n        // Second overload: (accessToken, groupId, memberUserIds)\r\n        memberUserIds = adminDataOrUserIds;\r\n      } else {\r\n        // First overload: (accessToken, groupId, adminData)\r\n        memberUserIds = adminDataOrUserIds.member_user_ids;\r\n      }\r\n\r\n      // Validate input\r\n      if (!memberUserIds || memberUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"Member user IDs list cannot be empty\", -1);\r\n      }\r\n\r\n      const requestData = {\r\n        group_id: groupId,\r\n        member_user_ids: memberUserIds,\r\n      };\r\n\r\n      const response = await this.client.apiPost<{\r\n        error: number;\r\n        message: string;\r\n      }>(this.endpoints.group.removeAdmins, accessToken, requestData);\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(error, \"Failed to remove admins\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Delete group chat (Disband group)\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID to delete\r\n   * @returns Delete result\r\n   */\r\n  async deleteGroup(\r\n    accessToken: string,\r\n    groupId: string\r\n  ): Promise<{ error: number; message: string }> {\r\n    try {\r\n      const requestData: GroupDeleteRequest = {\r\n        group_id: groupId,\r\n      };\r\n\r\n      const response = await this.client.apiPost<{\r\n        error: number;\r\n        message: string;\r\n      }>(this.endpoints.group.delete, accessToken, requestData);\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(error, \"Failed to delete group\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get list of OA groups\r\n   * @param accessToken OA access token\r\n   * @param offset Offset for pagination (default: 0)\r\n   * @param count Maximum number to return (default: 5, max: 50)\r\n   * @returns List of OA groups\r\n   *\r\n   * API: GET https://openapi.zalo.me/v3.0/oa/group/getgroupsofoa\r\n   */\r\n  async getGroupsOfOA(\r\n    accessToken: string,\r\n    offset: number = 0,\r\n    count: number = 5\r\n  ): Promise<GroupsOfOAResponse> {\r\n    try {\r\n      // Validate access token\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      // Validate parameters\r\n      if (offset < 0) {\r\n        throw new ZaloSDKError(\"Offset must be >= 0\", -1);\r\n      }\r\n\r\n      if (count <= 0 || count > 50) {\r\n        throw new ZaloSDKError(\"Count must be from 1 to 50\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<GroupsOfOAResponse>(\r\n        this.endpoints.group.getGroupsOfOA,\r\n        accessToken,\r\n        {\r\n          offset: offset,\r\n          count: count,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get groups of OA\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ALL groups of OA (custom function with automatic pagination)\r\n   * @param accessToken OA access token\r\n   * @param maxGroups Maximum number of groups to retrieve (default: unlimited)\r\n   * @param progressCallback Optional callback to track progress\r\n   * @returns All OA groups with pagination info\r\n   *\r\n   * This function automatically handles pagination to retrieve all groups,\r\n   * not limited by the 50-group API limit per request.\r\n   */\r\n  async getAllGroupsOfOA(\r\n    accessToken: string,\r\n    maxGroups?: number,\r\n    progressCallback?: (progress: { currentCount: number; totalCount?: number; percentage?: number; isComplete: boolean }) => void\r\n  ): Promise<AllGroupsOfOAResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      if (maxGroups !== undefined && maxGroups <= 0) {\r\n        throw new ZaloSDKError(\"Max groups must be greater than 0\", -1);\r\n      }\r\n\r\n      const allGroups: OAGroupItem[] = [];\r\n      let offset = 0;\r\n      const pageSize = 50; // Maximum allowed by Zalo API\r\n      let totalCount: number | undefined;\r\n      let pagesFetched = 0;\r\n      let hasMore = true;\r\n\r\n      // Progress tracking\r\n      const updateProgress = (currentCount: number, isComplete: boolean = false) => {\r\n        if (progressCallback) {\r\n          const percentage = totalCount ? Math.round((currentCount / totalCount) * 100) : undefined;\r\n          progressCallback({\r\n            currentCount,\r\n            totalCount,\r\n            percentage,\r\n            isComplete\r\n          });\r\n        }\r\n      };\r\n\r\n      // Initial progress\r\n      updateProgress(0);\r\n\r\n      while (hasMore) {\r\n        // Check if we've reached the maximum limit\r\n        if (maxGroups && allGroups.length >= maxGroups) {\r\n          break;\r\n        }\r\n\r\n        // Calculate how many groups to request in this batch\r\n        const remainingLimit = maxGroups ? maxGroups - allGroups.length : pageSize;\r\n        const currentPageSize = Math.min(pageSize, remainingLimit);\r\n\r\n        // Fetch current page\r\n        const response = await this.getGroupsOfOA(\r\n          accessToken,\r\n          offset,\r\n          currentPageSize\r\n        );\r\n\r\n        // Handle API errors\r\n        if (response.error !== 0) {\r\n          throw new ZaloSDKError(\r\n            `Failed to fetch groups at offset ${offset}: ${response.message}`,\r\n            response.error\r\n          );\r\n        }\r\n\r\n        // Check if we have data\r\n        if (!response.data || !response.data.groups) {\r\n          break;\r\n        }\r\n\r\n        // Update total count from first response\r\n        if (totalCount === undefined && response.data.total !== undefined) {\r\n          totalCount = response.data.total;\r\n\r\n          // If maxGroups is set and less than total, use maxGroups as effective total\r\n          if (maxGroups && maxGroups < totalCount) {\r\n            totalCount = maxGroups;\r\n          }\r\n        }\r\n\r\n        // Add groups to our collection\r\n        allGroups.push(...response.data.groups);\r\n        pagesFetched++;\r\n\r\n        // Update progress\r\n        updateProgress(allGroups.length);\r\n\r\n        // Check if we should continue\r\n        const receivedCount = response.data.groups.length;\r\n        hasMore = receivedCount === currentPageSize &&\r\n          (!maxGroups || allGroups.length < maxGroups) &&\r\n          (response.data.total === undefined || allGroups.length < response.data.total);\r\n\r\n        // Move to next page\r\n        offset += receivedCount;\r\n\r\n        // Safety check to prevent infinite loops\r\n        if (pagesFetched > 100) { // Max 5,000 groups (50 * 100)\r\n          console.warn(`getAllGroupsOfOA: Reached maximum page limit (${pagesFetched}) for OA`);\r\n          break;\r\n        }\r\n      }\r\n\r\n      // Final progress update\r\n      updateProgress(allGroups.length, true);\r\n\r\n      // Apply maxGroups limit if specified\r\n      const finalGroups = maxGroups ? allGroups.slice(0, maxGroups) : allGroups;\r\n\r\n      // Return enhanced response\r\n      return {\r\n        error: 0,\r\n        message: \"Success\",\r\n        data: {\r\n          total_groups: totalCount || allGroups.length,\r\n          groups: finalGroups,\r\n          pages_fetched: pagesFetched,\r\n          is_complete: !maxGroups || allGroups.length <= maxGroups\r\n        }\r\n      };\r\n\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get all groups of OA\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ALL groups of OA with simple interface (no progress tracking)\r\n   * @param accessToken OA access token\r\n   * @param maxGroups Maximum number of groups to retrieve (optional)\r\n   * @returns Array of all OA groups\r\n   *\r\n   * Simplified version of getAllGroupsOfOA that returns just the groups array\r\n   */\r\n  async getAllGroupsOfOASimple(\r\n    accessToken: string,\r\n    maxGroups?: number\r\n  ): Promise<OAGroupItem[]> {\r\n    const response = await this.getAllGroupsOfOA(accessToken, maxGroups);\r\n    return response.data.groups;\r\n  }\r\n\r\n  /**\r\n   * Get group quota information and asset_id\r\n   * @param accessToken OA access token\r\n   * @param productType Product type (optional)\r\n   * @param quotaType Quota type (optional)\r\n   * @returns Group quota information including asset_id\r\n   *\r\n   * API: POST https://openapi.zalo.me/v3.0/oa/quota/group\r\n   */\r\n  async getGroupQuota(\r\n    accessToken: string,\r\n    productType?: GMFProductType,\r\n    quotaType?: QuotaType\r\n  ): Promise<GroupQuotaMessageResponse> {\r\n    try {\r\n      // Validate access token\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      const quotaRequest: GroupQuotaMessageRequest = {\r\n        quota_owner: \"OA\",\r\n        ...(productType && { product_type: productType }),\r\n        ...(quotaType && { quota_type: quotaType }),\r\n      };\r\n\r\n      const response = await this.client.apiPost<GroupQuotaMessageResponse>(\r\n        this.endpoints.group.quota,\r\n        accessToken,\r\n        quotaRequest\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(error, \"Failed to get group quota\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get asset_id for creating GMF group\r\n   * @param accessToken OA access token\r\n   * @returns Asset_id for group creation\r\n   */\r\n  async getAssetId(accessToken: string): Promise<string> {\r\n    try {\r\n      const quotaRequest: GroupQuotaMessageRequest = {\r\n        quota_owner: \"OA\",\r\n      };\r\n\r\n      const response = await this.client.apiPost<GroupQuotaMessageResponse>(\r\n        this.endpoints.group.quota,\r\n        accessToken,\r\n        quotaRequest\r\n      );\r\n\r\n      if (response.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          response.message || \"Failed to get asset_id\",\r\n          response.error\r\n        );\r\n      }\r\n\r\n      if (response.data && response.data.length > 0) {\r\n        const activeAsset = response.data.find(\r\n          (asset) => asset.status === \"available\"\r\n        );\r\n\r\n        if (activeAsset) {\r\n          return activeAsset.asset_id;\r\n        }\r\n      }\r\n\r\n      throw new ZaloSDKError(\r\n        \"No valid asset_id found for group creation. Please check your GMF package.\",\r\n        -1\r\n      );\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get asset_id for group creation\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get list of asset_ids available for creating GMF groups\r\n   * @param accessToken OA access token\r\n   * @returns List of asset_ids and quota information\r\n   */\r\n  async getAssetIds(accessToken: string): Promise<GroupQuotaMessageResponse> {\r\n    try {\r\n      const quotaRequest: GroupQuotaMessageRequest = {\r\n        quota_owner: \"OA\",\r\n      };\r\n\r\n      const response = await this.client.apiPost<GroupQuotaMessageResponse>(\r\n        this.endpoints.group.quota,\r\n        accessToken,\r\n        quotaRequest\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get asset_ids list\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get list of recent chats\r\n   * @param accessToken OA access token\r\n   * @param offset Offset for pagination (default: 0)\r\n   * @param count Maximum number to return (default: 5, max: 50)\r\n   * @returns List of recent chats\r\n   *\r\n   * API: GET https://openapi.zalo.me/v3.0/oa/group/listrecentchat\r\n   */\r\n  async getRecentChats(\r\n    accessToken: string,\r\n    offset: number = 0,\r\n    count: number = 5\r\n  ): Promise<RecentChatsResponse> {\r\n    try {\r\n      // Validate access token\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      // Validate parameters\r\n      if (offset < 0) {\r\n        throw new ZaloSDKError(\"Offset must be >= 0\", -1);\r\n      }\r\n\r\n      if (count <= 0 || count > 50) {\r\n        throw new ZaloSDKError(\"Count must be from 1 to 50\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<RecentChatsResponse>(\r\n        this.endpoints.group.recent,\r\n        accessToken,\r\n        {\r\n          offset: offset,\r\n          count: count,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get recent chats\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Lấy thông tin tin nhắn trong một nhóm\r\n   * \r\n   * Lưu ý: Ứng dụng cần được cấp quyền quản lý thông tin nhóm.\r\n   * \r\n   * @param accessToken Access token của Official Account\r\n   * @param groupId ID nhóm muốn query\r\n   * @param offset Offset muốn query (mặc định: 0)\r\n   * @param count Số lượng mong muốn query (mặc định: 5)\r\n   * @returns Lịch sử tin nhắn trong nhóm\r\n   * \r\n   * @example\r\n   * ```typescript\r\n   * const messages = await groupService.getGroupConversation(\r\n   *   accessToken,\r\n   *   'f414c8f76fa586fbdfb4',\r\n   *   0,\r\n   *   2\r\n   * );\r\n   * ```\r\n   * \r\n   * API: GET https://openapi.zalo.me/v3.0/oa/group/conversation\r\n   */\r\n  async getGroupConversation(\r\n    accessToken: string,\r\n    groupId: string,\r\n    offset: number = 0,\r\n    count: number = 5\r\n  ): Promise<GroupConversationResponse> {\r\n    try {\r\n      // Validate access token\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      // Validate group ID\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      // Validate parameters\r\n      if (offset < 0) {\r\n        throw new ZaloSDKError(\"Offset must be >= 0\", -1);\r\n      }\r\n\r\n      if (count <= 0) {\r\n        throw new ZaloSDKError(\"Count must be greater than 0\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<GroupConversationResponse>(\r\n        this.endpoints.group.conversation,\r\n        accessToken,\r\n        {\r\n          group_id: groupId.trim(),\r\n          offset: offset,\r\n          count: count,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get group conversation\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get group members list from Zalo API\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param offset Offset for pagination (default: 0)\r\n   * @param count Maximum number to return (default: 5, max: 50)\r\n   * @returns Group members list\r\n   */\r\n  async getGroupMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    offset: number = 0,\r\n    count: number = 5\r\n  ): Promise<GroupMembersResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (offset < 0) {\r\n        throw new ZaloSDKError(\"Offset must be >= 0\", -1);\r\n      }\r\n\r\n      if (count <= 0 || count > 50) {\r\n        throw new ZaloSDKError(\"Count must be from 1 to 50\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<GroupMembersResponse>(\r\n        this.endpoints.group.members,\r\n        accessToken,\r\n        {\r\n          group_id: groupId.trim(),\r\n          offset: offset,\r\n          count: count,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get group members\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Find a specific group member by user ID\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param userId User ID to search for\r\n   * @returns GroupMemberInfo if found, null otherwise\r\n   * \r\n   * Note: This method iterates through pages of members to find the user.\r\n   */\r\n  async getGroupMemberByUserId(\r\n    accessToken: string,\r\n    groupId: string,\r\n    userId: string\r\n  ): Promise<GroupMemberInfo | null> {\r\n    try {\r\n      // Validate input\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (!userId || userId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"User ID cannot be empty\", -1);\r\n      }\r\n\r\n      const targetUserId = userId.trim();\r\n      let offset = 0;\r\n      const pageSize = 50; // Use max page size for efficiency\r\n      let hasMore = true;\r\n      let pagesFetched = 0;\r\n\r\n      while (hasMore) {\r\n        const response = await this.getGroupMembers(\r\n          accessToken,\r\n          groupId,\r\n          offset,\r\n          pageSize\r\n        );\r\n\r\n        if (response.error !== 0 || !response.data || !response.data.members) {\r\n          throw new ZaloSDKError(\r\n            `Failed to fetch members at offset ${offset}: ${response.message}`,\r\n            response.error\r\n          );\r\n        }\r\n\r\n        // Search in current page\r\n        const foundMember = response.data.members.find(\r\n          (member) => member.user_id === targetUserId\r\n        );\r\n\r\n        if (foundMember) {\r\n          return foundMember;\r\n        }\r\n\r\n        // Check if we should continue\r\n        const membersCount = response.data.members.length;\r\n        hasMore = membersCount === pageSize &&\r\n          (response.data.total === undefined || offset + membersCount < response.data.total);\r\n\r\n        offset += membersCount;\r\n        pagesFetched++;\r\n\r\n        // Safety break\r\n        if (pagesFetched > 1000) { // Max 50k members safety limit\r\n          break;\r\n        }\r\n      }\r\n\r\n      return null;\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        `Failed to find member with user ID ${userId}`\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ALL members of a group (custom function with automatic pagination)\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param maxMembers Maximum number of members to retrieve (default: unlimited)\r\n   * @param progressCallback Optional callback to track progress\r\n   * @returns All group members with pagination info\r\n   *\r\n   * This function automatically handles pagination to retrieve all members,\r\n   * not limited by the 50-member API limit per request.\r\n   */\r\n  async getAllGroupMembers(\r\n    accessToken: string,\r\n    groupId: string,\r\n    maxMembers?: number,\r\n    progressCallback?: (progress: GetAllMembersProgress) => void\r\n  ): Promise<AllGroupMembersResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (maxMembers !== undefined && maxMembers <= 0) {\r\n        throw new ZaloSDKError(\"Max members must be greater than 0\", -1);\r\n      }\r\n\r\n      const allMembers: GroupMemberInfo[] = [];\r\n      let offset = 0;\r\n      const pageSize = 50; // Maximum allowed by Zalo API\r\n      let totalCount: number | undefined;\r\n      let pagesFetched = 0;\r\n      let hasMore = true;\r\n\r\n      // Progress tracking\r\n      const updateProgress = (currentCount: number, isComplete: boolean = false) => {\r\n        if (progressCallback) {\r\n          const percentage = totalCount ? Math.round((currentCount / totalCount) * 100) : undefined;\r\n          progressCallback({\r\n            currentCount,\r\n            totalCount,\r\n            percentage,\r\n            isComplete\r\n          });\r\n        }\r\n      };\r\n\r\n      // Initial progress\r\n      updateProgress(0);\r\n\r\n      while (hasMore) {\r\n        // Check if we've reached the maximum limit\r\n        if (maxMembers && allMembers.length >= maxMembers) {\r\n          break;\r\n        }\r\n\r\n        // Calculate how many members to request in this batch\r\n        const remainingLimit = maxMembers ? maxMembers - allMembers.length : pageSize;\r\n        const currentPageSize = Math.min(pageSize, remainingLimit);\r\n\r\n        // Fetch current page\r\n        const response = await this.getGroupMembers(\r\n          accessToken,\r\n          groupId,\r\n          offset,\r\n          currentPageSize\r\n        );\r\n\r\n        // Handle API errors\r\n        if (response.error !== 0) {\r\n          throw new ZaloSDKError(\r\n            `Failed to fetch members at offset ${offset}: ${response.message}`,\r\n            response.error\r\n          );\r\n        }\r\n\r\n        // Check if we have data\r\n        if (!response.data || !response.data.members) {\r\n          break;\r\n        }\r\n\r\n        // Update total count from first response\r\n        if (totalCount === undefined && response.data.total !== undefined) {\r\n          totalCount = response.data.total;\r\n\r\n          // If maxMembers is set and less than total, use maxMembers as effective total\r\n          if (maxMembers && maxMembers < totalCount) {\r\n            totalCount = maxMembers;\r\n          }\r\n        }\r\n\r\n        // Add members to our collection\r\n        allMembers.push(...response.data.members);\r\n        pagesFetched++;\r\n\r\n        // Update progress\r\n        updateProgress(allMembers.length);\r\n\r\n        // Check if we should continue\r\n        const receivedCount = response.data.members.length;\r\n        hasMore = receivedCount === currentPageSize &&\r\n          (!maxMembers || allMembers.length < maxMembers) &&\r\n          (response.data.total === undefined || allMembers.length < response.data.total);\r\n\r\n        // Move to next page\r\n        offset += receivedCount;\r\n\r\n        // Safety check to prevent infinite loops\r\n        if (pagesFetched > 1000) { // Max 50,000 members (50 * 1000)\r\n          console.warn(`getAllGroupMembers: Reached maximum page limit (${pagesFetched}) for group ${groupId}`);\r\n          break;\r\n        }\r\n      }\r\n\r\n      // Final progress update\r\n      updateProgress(allMembers.length, true);\r\n\r\n      // Apply maxMembers limit if specified\r\n      const finalMembers = maxMembers ? allMembers.slice(0, maxMembers) : allMembers;\r\n\r\n      // Return enhanced response\r\n      return {\r\n        error: 0,\r\n        message: \"Success\",\r\n        data: {\r\n          total_members: totalCount || allMembers.length,\r\n          members: finalMembers,\r\n          pages_fetched: pagesFetched,\r\n          is_complete: !maxMembers || allMembers.length <= maxMembers\r\n        }\r\n      };\r\n\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get all group members\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ALL members of a group with simple interface (no progress tracking)\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param maxMembers Maximum number of members to retrieve (optional)\r\n   * @returns Array of all group members\r\n   *\r\n   * Simplified version of getAllGroupMembers that returns just the members array\r\n   */\r\n  async getAllGroupMembersSimple(\r\n    accessToken: string,\r\n    groupId: string,\r\n    maxMembers?: number\r\n  ): Promise<GroupMemberInfo[]> {\r\n    const response = await this.getAllGroupMembers(accessToken, groupId, maxMembers);\r\n    return response.data.members;\r\n  }\r\n\r\n  /**\r\n   * Get ALL members of a group with DETAILED user information (advanced API)\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param maxMembers Maximum number of members to retrieve (default: unlimited)\r\n   * @param progressCallback Optional callback to track progress\r\n   * @param options Advanced options for fetching details\r\n   * @returns All group members with detailed user information\r\n   *\r\n   * This function:\r\n   * 1. First gets all basic member info using getAllGroupMembers()\r\n   * 2. Then fetches detailed user info for each member using UserService.getUserInfo()\r\n   * 3. Combines both basic and detailed info into EnhancedGroupMember objects\r\n   * 4. If detailed info fails to fetch, falls back to basic info only\r\n   * 5. Provides detailed progress tracking and statistics\r\n   */\r\n  async getAllGroupMembersWithDetails(\r\n    accessToken: string,\r\n    groupId: string,\r\n    maxMembers?: number,\r\n    progressCallback?: (progress: GetAllMembersWithDetailsProgress) => void,\r\n    options?: {\r\n      /** Batch size for fetching user details (default: 10) */\r\n      detailBatchSize?: number;\r\n      /** Timeout for each user detail request in ms (default: 5000) */\r\n      detailTimeout?: number;\r\n      /** Whether to continue if UserService is not available (default: true) */\r\n      continueWithoutUserService?: boolean;\r\n      /** Maximum concurrent detail requests (default: 5) */\r\n      maxConcurrentRequests?: number;\r\n    }\r\n  ): Promise<AllGroupMembersWithDetailsResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      if (!groupId || groupId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Group ID cannot be empty\", -1);\r\n      }\r\n\r\n      // Set default options\r\n      const opts = {\r\n        detailBatchSize: 10,\r\n        detailTimeout: 5000,\r\n        continueWithoutUserService: true,\r\n        maxConcurrentRequests: 5,\r\n        ...options\r\n      };\r\n\r\n      // Check if UserService is available\r\n      if (!this.userService && !opts.continueWithoutUserService) {\r\n        throw new ZaloSDKError(\r\n          \"UserService is required for fetching detailed member information. \" +\r\n          \"Please provide UserService in constructor or set continueWithoutUserService to true.\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Progress tracking helper\r\n      const updateProgress = (\r\n        currentCount: number,\r\n        totalCount: number | undefined,\r\n        phase: GetAllMembersWithDetailsProgress['phase'],\r\n        phaseDetails?: GetAllMembersWithDetailsProgress['phase_details'],\r\n        isComplete: boolean = false\r\n      ) => {\r\n        if (progressCallback) {\r\n          const percentage = totalCount ? Math.round((currentCount / totalCount) * 100) : undefined;\r\n          progressCallback({\r\n            currentCount,\r\n            totalCount,\r\n            percentage,\r\n            isComplete,\r\n            phase,\r\n            phase_details: phaseDetails\r\n          });\r\n        }\r\n      };\r\n\r\n      // Phase 1: Get all basic member information\r\n      updateProgress(0, undefined, 'fetching_members');\r\n\r\n      const basicMembersResponse = await this.getAllGroupMembers(\r\n        accessToken,\r\n        groupId,\r\n        maxMembers,\r\n        (basicProgress) => {\r\n          updateProgress(\r\n            basicProgress.currentCount,\r\n            basicProgress.totalCount,\r\n            'fetching_members'\r\n          );\r\n        }\r\n      );\r\n\r\n      const basicMembers = basicMembersResponse.data.members;\r\n      const totalMembers = basicMembers.length;\r\n\r\n      // Initialize enhanced members with basic info\r\n      const enhancedMembers: EnhancedGroupMember[] = basicMembers.map((member: GroupMemberInfo) => ({\r\n        basic_info: member,\r\n        detailed_info: null as UserInfo | null,\r\n        has_detailed_info: false\r\n      }));\r\n\r\n      // Statistics tracking\r\n      let successfulDetails = 0;\r\n      let failedDetails = 0;\r\n\r\n      // Phase 2: Fetch detailed user information (if UserService is available)\r\n      if (this.userService && totalMembers > 0) {\r\n        updateProgress(0, totalMembers, 'fetching_details', {\r\n          details_fetched: 0,\r\n          details_failed: 0,\r\n          current_batch: 0,\r\n          total_batches: Math.ceil(totalMembers / opts.detailBatchSize)\r\n        });\r\n\r\n        // Process members in batches to avoid overwhelming the API\r\n        for (let i = 0; i < totalMembers; i += opts.detailBatchSize) {\r\n          const batch = enhancedMembers.slice(i, i + opts.detailBatchSize);\r\n          const currentBatch = Math.floor(i / opts.detailBatchSize) + 1;\r\n          const totalBatches = Math.ceil(totalMembers / opts.detailBatchSize);\r\n\r\n          // Create promises for current batch with concurrency limit\r\n          const batchPromises = batch.map(async (enhancedMember) => {\r\n            const userId = enhancedMember.basic_info.user_id || enhancedMember.basic_info.oa_id;\r\n\r\n            if (!userId) {\r\n              enhancedMember.detail_fetch_error = \"No user_id or oa_id available\";\r\n              failedDetails++;\r\n              return;\r\n            }\r\n\r\n            try {\r\n              // Create timeout promise\r\n              const timeoutPromise = new Promise<never>((_, reject) => {\r\n                setTimeout(() => reject(new Error('Timeout')), opts.detailTimeout);\r\n              });\r\n\r\n              // Fetch user details with timeout\r\n              const userDetailPromise = this.userService!.getUserInfo(accessToken, userId);\r\n              const userDetail = await Promise.race([userDetailPromise, timeoutPromise]);\r\n\r\n              enhancedMember.detailed_info = userDetail;\r\n              enhancedMember.has_detailed_info = true;\r\n              successfulDetails++;\r\n\r\n            } catch (error) {\r\n              const errorMessage = error instanceof Error ? error.message : 'Unknown error';\r\n              enhancedMember.detail_fetch_error = `Failed to fetch user details: ${errorMessage}`;\r\n              failedDetails++;\r\n            }\r\n\r\n            // Update progress for each member processed\r\n            const processedCount = successfulDetails + failedDetails;\r\n            updateProgress(processedCount, totalMembers, 'fetching_details', {\r\n              details_fetched: successfulDetails,\r\n              details_failed: failedDetails,\r\n              current_batch: currentBatch,\r\n              total_batches: totalBatches\r\n            });\r\n          });\r\n\r\n          // Process batch with concurrency limit\r\n          const chunks = [];\r\n          for (let j = 0; j < batchPromises.length; j += opts.maxConcurrentRequests) {\r\n            chunks.push(batchPromises.slice(j, j + opts.maxConcurrentRequests));\r\n          }\r\n\r\n          for (const chunk of chunks) {\r\n            await Promise.all(chunk);\r\n          }\r\n        }\r\n      } else if (!this.userService) {\r\n        // UserService not available, mark all as failed but continue\r\n        failedDetails = totalMembers;\r\n        enhancedMembers.forEach(member => {\r\n          member.detail_fetch_error = \"UserService not available\";\r\n        });\r\n      }\r\n\r\n      // Final progress update\r\n      updateProgress(totalMembers, totalMembers, 'complete', {\r\n        details_fetched: successfulDetails,\r\n        details_failed: failedDetails\r\n      }, true);\r\n\r\n      // Calculate success rate\r\n      const successRate = totalMembers > 0 ? Math.round((successfulDetails / totalMembers) * 100) : 0;\r\n\r\n      // Apply maxMembers limit if specified\r\n      const finalMembers = maxMembers ? enhancedMembers.slice(0, maxMembers) : enhancedMembers;\r\n\r\n      // Return enhanced response\r\n      return {\r\n        error: 0,\r\n        message: \"Success\",\r\n        data: {\r\n          total_members: basicMembersResponse.data.total_members,\r\n          members: finalMembers,\r\n          pages_fetched: basicMembersResponse.data.pages_fetched,\r\n          is_complete: basicMembersResponse.data.is_complete,\r\n          detail_fetch_stats: {\r\n            total_processed: totalMembers,\r\n            successful_details: successfulDetails,\r\n            failed_details: failedDetails,\r\n            success_rate: successRate\r\n          }\r\n        }\r\n      };\r\n\r\n    } catch (error) {\r\n      throw this.handleGroupManagementError(\r\n        error,\r\n        \"Failed to get all group members with details\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get ALL members of a group with detailed info - Simple interface\r\n   * @param accessToken OA access token\r\n   * @param groupId Group ID\r\n   * @param maxMembers Maximum number of members to retrieve (optional)\r\n   * @returns Array of enhanced group members with detailed info\r\n   *\r\n   * Simplified version that returns just the enhanced members array\r\n   */\r\n  async getAllGroupMembersWithDetailsSimple(\r\n    accessToken: string,\r\n    groupId: string,\r\n    maxMembers?: number\r\n  ): Promise<EnhancedGroupMember[]> {\r\n    const response = await this.getAllGroupMembersWithDetails(accessToken, groupId, maxMembers);\r\n    return response.data.members;\r\n  }\r\n\r\n  private handleGroupManagementError(\r\n    error: any,\r\n    defaultMessage: string\r\n  ): Error {\r\n    if (error instanceof ZaloSDKError) {\r\n      return error;\r\n    }\r\n    if (error.response?.data) {\r\n      const errorData = error.response.data;\r\n      return new ZaloSDKError(\r\n        `${defaultMessage}: ${errorData.message || errorData.error || \"Unknown error\"\r\n        }`,\r\n        errorData.error || -1,\r\n        errorData\r\n      );\r\n    }\r\n    return new ZaloSDKError(\r\n      `${defaultMessage}: ${error.message || \"Unknown error\"}`,\r\n      -1,\r\n      error\r\n    );\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  ArticleRequest,\r\n  ArticleNormalRequest,\r\n  ArticleVideoRequest,\r\n  ArticleResponse,\r\n  ArticleProcessResponse,\r\n  ArticleVerifyResponse,\r\n  ArticleDetail,\r\n  ArticleDetailNormal,\r\n  ArticleDetailVideo,\r\n  ArticleListRequest,\r\n  ArticleListResponse,\r\n  ArticleListItem,\r\n  ArticleRemoveRequest,\r\n  ArticleRemoveResponse,\r\n  ArticleUpdateRequest,\r\n  ArticleUpdateNormalRequest,\r\n  ArticleUpdateVideoRequest,\r\n  ArticleUpdateResponse,\r\n  ArticleCover,\r\n  ArticleBodyItem,\r\n  ARTICLE_CONSTRAINTS,\r\n  ArticleStatus,\r\n  CommentStatus,\r\n  ArticleType,\r\n  CoverType,\r\n  BodyItemType,\r\n  BulkRemoveItemResult,\r\n  BulkRemoveProgress,\r\n  BulkRemoveOptions,\r\n  BulkRemoveResult,\r\n  BulkDetailItemResult,\r\n  BulkDetailProgress,\r\n  BulkDetailOptions,\r\n  BulkDetailResult,\r\n} from \"../types/article\";\r\nimport { ZaloSDKError, ZaloResponse } from \"../types/common\";\r\n\r\n/**\r\n * Service for handling Zalo Official Account Article Management APIs\r\n *\r\n * CONDITIONS FOR USING ZALO ARTICLE MANAGEMENT:\r\n *\r\n * 1. GENERAL CONDITIONS:\r\n *    - OA must have permission to create articles\r\n *    - Access token must have \"manage_article\" scope\r\n *    - OA must have active status and be verified\r\n *\r\n * 2. ARTICLE CREATION:\r\n *    - Title: required, max 150 characters\r\n *    - Author: required for normal articles, max 50 characters\r\n *    - Description: required, max 300 characters\r\n *    - Image size: max 1MB per image\r\n *    - Status: 'show' (publish immediately) or 'hide' (draft)\r\n *    - Comment: 'show' (allow comments) or 'hide' (disable comments)\r\n *\r\n * 3. CONTENT VALIDATION:\r\n *    - Normal articles: must have cover, body content, and author\r\n *    - Video articles: must have video_id and avatar\r\n *    - Body items: support text, image, video, and product types\r\n *    - Tracking links: must be valid URLs\r\n *\r\n * 4. LIMITS AND CONSTRAINTS:\r\n *    - Article list: max 10 items per request\r\n *    - Processing time: use token to check progress\r\n *    - Update frequency: reasonable intervals recommended\r\n */\r\nexport class ArticleService {\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    article: {\r\n      create: \"https://openapi.zalo.me/v2.0/article/create\",\r\n      update: \"https://openapi.zalo.me/v2.0/article/update\",\r\n      remove: \"https://openapi.zalo.me/v2.0/article/remove\",\r\n      verify: \"https://openapi.zalo.me/v2.0/article/verify\",\r\n      detail: \"https://openapi.zalo.me/v2.0/article/getdetail\",\r\n      list: \"https://openapi.zalo.me/v2.0/article/getslice\",\r\n    },\r\n  } as const;\r\n\r\n  constructor(private readonly client: ZaloClient) { }\r\n\r\n  /**\r\n   * Create normal article with rich content\r\n   * @param accessToken OA access token\r\n   * @param request Normal article information\r\n   * @returns Token for tracking creation progress\r\n   */\r\n  async createNormalArticle(\r\n    accessToken: string,\r\n    request: ArticleNormalRequest\r\n  ): Promise<ArticleResponse> {\r\n    try {\r\n      // Validate input\r\n      this.validateNormalArticleRequest(request);\r\n\r\n      // Process tracking link - use default if not provided or invalid\r\n      let processedTrackingLink = \"https://v2.redai.vn/auth\";\r\n      if (\r\n        request.tracking_link &&\r\n        this.isValidTrackingLink(request.tracking_link)\r\n      ) {\r\n        processedTrackingLink = request.tracking_link;\r\n      }\r\n\r\n      const data = {\r\n        type: request.type,\r\n        title: request.title,\r\n        author: request.author,\r\n        cover: request.cover,\r\n        description: request.description,\r\n        body: request.body,\r\n        related_medias: request.related_medias || [],\r\n        tracking_link: processedTrackingLink,\r\n        status: request.status || ArticleStatus.HIDE,\r\n        comment: request.comment || CommentStatus.SHOW,\r\n      };\r\n\r\n      // Gọi API và nhận đúng cấu trúc bao bọc của Zalo\r\n      const response = await this.client.apiPost<ZaloResponse<{ token: string }>>(\r\n        this.endpoints.article.create,\r\n        accessToken,\r\n        data\r\n      );\r\n      // Trả về đầy đủ cấu trúc response theo type chuẩn\r\n      if (!response.data || !response.data.token) {\r\n        throw new ZaloSDKError(\"No token received from article creation\", -1, response);\r\n      }\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to create normal article\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Create video article\r\n   * @param accessToken OA access token\r\n   * @param request Video article information\r\n   * @returns Token for tracking creation progress\r\n   */\r\n  async createVideoArticle(\r\n    accessToken: string,\r\n    request: ArticleVideoRequest\r\n  ): Promise<ArticleResponse> {\r\n    try {\r\n      // Validate input\r\n      this.validateVideoArticleRequest(request);\r\n\r\n      const data = {\r\n        type: request.type,\r\n        title: request.title,\r\n        description: request.description,\r\n        video_id: request.video_id,\r\n        avatar: request.avatar,\r\n        status: request.status || ArticleStatus.HIDE,\r\n        comment: request.comment || CommentStatus.SHOW,\r\n      };\r\n\r\n      // Gọi API và nhận đúng cấu trúc bao bọc của Zalo\r\n      const response = await this.client.apiPost<ZaloResponse<{ token: string }>>(\r\n        this.endpoints.article.create,\r\n        accessToken,\r\n        data\r\n      );\r\n      // Trả về đầy đủ cấu trúc response theo type chuẩn\r\n      if (!response.data || !response.data.token) {\r\n        throw new ZaloSDKError(\"No token received from article creation\", -1, response);\r\n      }\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to create video article\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Create article (automatically detect type)\r\n   * @param accessToken OA access token\r\n   * @param request Article information\r\n   * @returns Token for tracking creation progress\r\n   */\r\n  async createArticle(\r\n    accessToken: string,\r\n    request: ArticleRequest\r\n  ): Promise<ArticleResponse> {\r\n    if (request.type === ArticleType.NORMAL) {\r\n      return this.createNormalArticle(accessToken, request);\r\n    } else if (request.type === ArticleType.VIDEO) {\r\n      return this.createVideoArticle(accessToken, request);\r\n    } else {\r\n      throw new ZaloSDKError(\r\n        'Invalid article type. Only \"normal\" and \"video\" are supported',\r\n        -1\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Check article creation progress\r\n   * @param accessToken OA access token\r\n   * @param token Token from article creation response\r\n   * @returns Progress information\r\n   */\r\n  async checkArticleProcess(\r\n    accessToken: string,\r\n    token: string\r\n  ): Promise<ArticleProcessResponse> {\r\n    try {\r\n      if (!token || token.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Token cannot be empty\", -1);\r\n      }\r\n\r\n      // Use verifyArticle method to check progress\r\n      const response = await this.verifyArticle(accessToken, token);\r\n\r\n      // Convert response to standard format\r\n      const result: ArticleProcessResponse = {\r\n        article_id: response?.id,\r\n        status: response?.id ? \"success\" : \"processing\",\r\n        created_time: Date.now(),\r\n        updated_time: Date.now(),\r\n      };\r\n\r\n      return result;\r\n    } catch (error) {\r\n      // Handle token expiration - means article processing is completed\r\n      if (\r\n        error instanceof ZaloSDKError &&\r\n        error.message.includes(\"token was expired\")\r\n      ) {\r\n        return {\r\n          status: \"success\",\r\n          error_message: \"Token expired - article processing completed\",\r\n          created_time: Date.now(),\r\n          updated_time: Date.now(),\r\n        };\r\n      }\r\n\r\n      // Handle API errors - article may still be processing\r\n      if (error instanceof ZaloSDKError && error.message.includes(\"API\")) {\r\n        return {\r\n          status: \"processing\",\r\n          created_time: Date.now(),\r\n          updated_time: Date.now(),\r\n        };\r\n      }\r\n\r\n      // Handle response structure errors\r\n      if (error instanceof TypeError) {\r\n        return {\r\n          status: \"processing\",\r\n          error_message: \"Article is being processed\",\r\n          created_time: Date.now(),\r\n          updated_time: Date.now(),\r\n        };\r\n      }\r\n\r\n      throw this.handleArticleError(error, \"Failed to check article process\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Verify article and get article ID\r\n   * @param accessToken OA access token\r\n   * @param token Token from article creation or update response\r\n   * @returns Article ID\r\n   */\r\n  async verifyArticle(\r\n    accessToken: string,\r\n    token: string\r\n  ): Promise<ArticleVerifyResponse> {\r\n    try {\r\n      if (!token || token.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Token cannot be empty\", -1);\r\n      }\r\n\r\n      const data = {\r\n        token: token,\r\n      };\r\n\r\n      const response = await this.client.apiPost<{ data: ArticleVerifyResponse }>(\r\n        this.endpoints.article.verify,\r\n        accessToken,\r\n        data\r\n      );\r\n\r\n      return response.data;\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to verify article\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get article details\r\n   * @param accessToken OA access token\r\n   * @param articleId Article ID\r\n   * @returns Article details\r\n   */\r\n  async getArticleDetail(\r\n    accessToken: string,\r\n    articleId: string\r\n  ): Promise<ArticleDetail> {\r\n    try {\r\n      if (!articleId || articleId.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Article ID cannot be empty\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<{ data: ArticleDetail }>(\r\n        this.endpoints.article.detail,\r\n        accessToken,\r\n        { id: articleId }\r\n      );\r\n\r\n      return response.data;\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to get article detail\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get article list\r\n   * @param accessToken OA access token\r\n   * @param request List parameters\r\n   * @returns Article list\r\n   */\r\n  async getArticleList(\r\n    accessToken: string,\r\n    request: ArticleListRequest\r\n  ): Promise<ArticleListResponse> {\r\n    try {\r\n      // Validate input\r\n      this.validateArticleListRequest(request);\r\n\r\n      const response = await this.client.apiGet<ArticleListResponse>(\r\n        this.endpoints.article.list,\r\n        accessToken,\r\n        {\r\n          offset: request.offset,\r\n          limit: request.limit,\r\n          type: request.type,\r\n        }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to get article list\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get all articles by automatically fetching all pages\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * // Get all normal articles with progress tracking\r\n   * const result = await articleService.getAllArticles(accessToken, \"normal\", {\r\n   *   batchSize: 50,\r\n   *   maxArticles: 1000,\r\n   *   onProgress: (progress) => {\r\n   *     console.log(`Batch ${progress.currentBatch}: ${progress.totalFetched} articles fetched`);\r\n   *   }\r\n   * });\r\n   *\r\n   * console.log(`Total: ${result.totalFetched} articles in ${result.totalBatches} batches`);\r\n   * console.log(`Has more: ${result.hasMore}`);\r\n   * ```\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param type Article type (\"normal\" or \"video\")\r\n   * @param options Configuration options for fetching\r\n   * @returns All articles with pagination info\r\n   */\r\n  async getAllArticles(\r\n    accessToken: string,\r\n    type: \"normal\" | \"video\" = \"normal\",\r\n    options: {\r\n      /** Number of articles to fetch per request (default: 10, max: 10) */\r\n      batchSize?: number;\r\n      /** Maximum number of articles to fetch (default: 1000, 0 = no limit) */\r\n      maxArticles?: number;\r\n      /** Optional callback to track progress */\r\n      onProgress?: (progress: {\r\n        currentBatch: number;\r\n        totalFetched: number;\r\n        hasMore: boolean;\r\n      }) => void;\r\n    } = {}\r\n  ): Promise<{\r\n    /** Array of all fetched articles */\r\n    articles: ArticleListItem[];\r\n    /** Total number of articles fetched */\r\n    totalFetched: number;\r\n    /** Number of API calls made */\r\n    totalBatches: number;\r\n    /** Whether there are more articles available */\r\n    hasMore: boolean;\r\n  }> {\r\n    try {\r\n      const {\r\n        batchSize = 10, // Zalo API maximum limit is 10\r\n        maxArticles = 1000,\r\n        onProgress\r\n      } = options;\r\n\r\n      // Validate parameters\r\n      if (batchSize <= 0 || batchSize > ARTICLE_CONSTRAINTS.LIST_MAX_LIMIT) {\r\n        throw new ZaloSDKError(\r\n          `Batch size must be between 1 and ${ARTICLE_CONSTRAINTS.LIST_MAX_LIMIT}`,\r\n          -1\r\n        );\r\n      }\r\n\r\n      if (maxArticles < 0) {\r\n        throw new ZaloSDKError(\"Max articles must be >= 0 (0 = no limit)\", -1);\r\n      }\r\n\r\n      if (![\"normal\", \"video\"].includes(type)) {\r\n        throw new ZaloSDKError('Type must be \"normal\" or \"video\"', -1);\r\n      }\r\n\r\n      const allArticles: ArticleListItem[] = [];\r\n      let offset = 0;\r\n      let currentBatch = 0;\r\n      let hasMore = true;\r\n\r\n      while (hasMore) {\r\n        currentBatch++;\r\n\r\n        // Calculate limit for this batch\r\n        let currentLimit = batchSize;\r\n        if (maxArticles > 0) {\r\n          const remaining = maxArticles - allArticles.length;\r\n          if (remaining <= 0) break;\r\n          currentLimit = Math.min(batchSize, remaining);\r\n        }\r\n\r\n        // Fetch current batch\r\n        const response = await this.getArticleList(accessToken, {\r\n          offset,\r\n          limit: currentLimit,\r\n          type\r\n        });\r\n\r\n        // Add articles to collection\r\n        // Check if response is successful and has data\r\n        if ('data' in response && response.data?.medias && response.data.medias.length > 0) {\r\n          allArticles.push(...response.data.medias);\r\n          offset += response.data.medias.length;\r\n\r\n          // Check if we have more data\r\n          hasMore = response.data.medias.length === currentLimit;\r\n\r\n          // Stop if we've reached max articles\r\n          if (maxArticles > 0 && allArticles.length >= maxArticles) {\r\n            hasMore = false;\r\n          }\r\n        } else if ('medias' in response && response.medias && response.medias.length > 0) {\r\n          // Handle direct response format (fallback)\r\n          allArticles.push(...response.medias);\r\n          offset += response.medias.length;\r\n\r\n          // Check if we have more data\r\n          hasMore = response.medias.length === currentLimit;\r\n\r\n          // Stop if we've reached max articles\r\n          if (maxArticles > 0 && allArticles.length >= maxArticles) {\r\n            hasMore = false;\r\n          }\r\n        } else {\r\n          hasMore = false;\r\n        }\r\n\r\n        // Call progress callback if provided\r\n        if (onProgress) {\r\n          onProgress({\r\n            currentBatch,\r\n            totalFetched: allArticles.length,\r\n            hasMore\r\n          });\r\n        }\r\n\r\n        // Safety check to prevent infinite loops\r\n        if (currentBatch > 100) {\r\n          console.warn(\"Reached maximum batch limit (100), stopping fetch\");\r\n          break;\r\n        }\r\n      }\r\n\r\n      return {\r\n        articles: allArticles,\r\n        totalFetched: allArticles.length,\r\n        totalBatches: currentBatch,\r\n        hasMore: hasMore && (maxArticles === 0 || allArticles.length < maxArticles)\r\n      };\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to get all articles\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get all articles of both types (normal and video) combined\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * // Get all articles (both normal and video)\r\n   * const result = await articleService.getAllArticlesCombined(accessToken, {\r\n   *   batchSize: 50,\r\n   *   maxArticlesPerType: 500,\r\n   *   onProgress: (progress) => {\r\n   *     console.log(`${progress.type}: Batch ${progress.currentBatch}, Total: ${progress.totalFetched}`);\r\n   *   }\r\n   * });\r\n   *\r\n   * console.log(`Total articles: ${result.totalFetched}`);\r\n   * console.log(`Normal articles: ${result.breakdown.normal.totalFetched}`);\r\n   * console.log(`Video articles: ${result.breakdown.video.totalFetched}`);\r\n   * ```\r\n   *\r\n   * @param accessToken OA access token\r\n   * @param options Configuration options for fetching\r\n   * @returns All articles (normal + video) with combined pagination info\r\n   */\r\n  async getAllArticlesCombined(\r\n    accessToken: string,\r\n    options: {\r\n      /** Number of articles to fetch per request for each type (default: 10, max: 10) */\r\n      batchSize?: number;\r\n      /** Maximum number of articles to fetch per type (default: 500, 0 = no limit) */\r\n      maxArticlesPerType?: number;\r\n      /** Optional callback to track progress */\r\n      onProgress?: (progress: {\r\n        type: \"normal\" | \"video\";\r\n        currentBatch: number;\r\n        totalFetched: number;\r\n        hasMore: boolean;\r\n      }) => void;\r\n    } = {}\r\n  ): Promise<{\r\n    /** Array of all fetched articles (normal + video) */\r\n    articles: ArticleListItem[];\r\n    /** Breakdown by type */\r\n    breakdown: {\r\n      normal: {\r\n        articles: ArticleListItem[];\r\n        totalFetched: number;\r\n        totalBatches: number;\r\n        hasMore: boolean;\r\n      };\r\n      video: {\r\n        articles: ArticleListItem[];\r\n        totalFetched: number;\r\n        totalBatches: number;\r\n        hasMore: boolean;\r\n      };\r\n    };\r\n    /** Total number of articles fetched */\r\n    totalFetched: number;\r\n    /** Total number of API calls made */\r\n    totalBatches: number;\r\n  }> {\r\n    try {\r\n      const {\r\n        batchSize = 10, // Zalo API maximum limit is 10\r\n        maxArticlesPerType = 500,\r\n        onProgress\r\n      } = options;\r\n\r\n      // Fetch normal articles\r\n      const normalResult = await this.getAllArticles(accessToken, \"normal\", {\r\n        batchSize,\r\n        maxArticles: maxArticlesPerType,\r\n        onProgress: onProgress ? (progress) => onProgress({\r\n          type: \"normal\",\r\n          ...progress\r\n        }) : undefined\r\n      });\r\n\r\n      // Fetch video articles\r\n      const videoResult = await this.getAllArticles(accessToken, \"video\", {\r\n        batchSize,\r\n        maxArticles: maxArticlesPerType,\r\n        onProgress: onProgress ? (progress) => onProgress({\r\n          type: \"video\",\r\n          ...progress\r\n        }) : undefined\r\n      });\r\n\r\n      // Combine results\r\n      const allArticles = [...normalResult.articles, ...videoResult.articles];\r\n\r\n      return {\r\n        articles: allArticles,\r\n        breakdown: {\r\n          normal: normalResult,\r\n          video: videoResult\r\n        },\r\n        totalFetched: allArticles.length,\r\n        totalBatches: normalResult.totalBatches + videoResult.totalBatches\r\n      };\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to get all articles combined\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Remove article\r\n   * @param accessToken OA access token\r\n   * @param articleId Article ID to remove\r\n   * @returns Removal result\r\n   */\r\n  async removeArticle(\r\n    accessToken: string,\r\n    articleId: string\r\n  ): Promise<ArticleRemoveResponse> {\r\n    try {\r\n      if (!articleId || articleId.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Article ID cannot be empty\", -1);\r\n      }\r\n\r\n      const data: ArticleRemoveRequest = {\r\n        id: articleId,\r\n      };\r\n\r\n      const response = await this.client.apiPost<ArticleRemoveResponse>(\r\n        this.endpoints.article.remove,\r\n        accessToken,\r\n        data\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      // Handle case where article may already be deleted\r\n      if (\r\n        error instanceof ZaloSDKError &&\r\n        error.message.includes(\"No data received\")\r\n      ) {\r\n        return {\r\n          message: \"Article has been deleted or does not exist\",\r\n        };\r\n      }\r\n\r\n      throw this.handleArticleError(error, \"Failed to remove article\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Remove multiple articles in bulk (advanced function with progress tracking)\r\n   * @param accessToken OA access token\r\n   * @param articleIds Array of article IDs to remove\r\n   * @param options Options for bulk removal operation\r\n   * @param progressCallback Optional callback to track progress\r\n   * @returns Complete bulk removal result with detailed statistics\r\n   *\r\n   * This function automatically handles:\r\n   * - Batch processing to avoid overwhelming the API\r\n   * - Concurrent requests with configurable limits\r\n   * - Error handling for individual articles\r\n   * - Progress tracking and statistics\r\n   * - Retry mechanism for failed requests\r\n   */\r\n  async removeBulkArticles(\r\n    accessToken: string,\r\n    articleIds: string[],\r\n    options?: BulkRemoveOptions,\r\n    progressCallback?: (progress: BulkRemoveProgress) => void\r\n  ): Promise<BulkRemoveResult> {\r\n    try {\r\n      // Validate input\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      if (!articleIds || articleIds.length === 0) {\r\n        throw new ZaloSDKError(\"Article IDs array cannot be empty\", -1);\r\n      }\r\n\r\n      // Filter out empty/invalid IDs\r\n      const validArticleIds = articleIds.filter(id => id && id.trim().length > 0);\r\n      if (validArticleIds.length === 0) {\r\n        throw new ZaloSDKError(\"No valid article IDs found\", -1);\r\n      }\r\n\r\n      // Set default options\r\n      const opts: Required<BulkRemoveOptions> = {\r\n        batch_size: 10,\r\n        max_concurrency: 3,\r\n        continue_on_error: true,\r\n        batch_delay: 100,\r\n        request_timeout: 10000,\r\n        ...options\r\n      };\r\n\r\n      // Initialize tracking variables\r\n      const results: BulkRemoveItemResult[] = [];\r\n      const errorSummary: { [errorType: string]: number } = {};\r\n      const startTime = Date.now();\r\n      let successfulCount = 0;\r\n      let failedCount = 0;\r\n\r\n      // Progress tracking helper\r\n      const updateProgress = (currentCount: number, isComplete: boolean = false) => {\r\n        if (progressCallback) {\r\n          const percentage = Math.round((currentCount / validArticleIds.length) * 100);\r\n          const currentBatch = Math.ceil(currentCount / opts.batch_size);\r\n          const totalBatches = Math.ceil(validArticleIds.length / opts.batch_size);\r\n\r\n          progressCallback({\r\n            current_count: currentCount,\r\n            total_count: validArticleIds.length,\r\n            percentage,\r\n            successful_count: successfulCount,\r\n            failed_count: failedCount,\r\n            is_complete: isComplete,\r\n            current_batch: currentBatch,\r\n            total_batches: totalBatches\r\n          });\r\n        }\r\n      };\r\n\r\n      // Initial progress\r\n      updateProgress(0);\r\n\r\n      // Process articles in batches\r\n      for (let i = 0; i < validArticleIds.length; i += opts.batch_size) {\r\n        const batch = validArticleIds.slice(i, i + opts.batch_size);\r\n\r\n        // Create promises for current batch with concurrency limit\r\n        const batchPromises = batch.map(async (articleId) => {\r\n          const itemStartTime = Date.now();\r\n\r\n          try {\r\n            // Create timeout promise\r\n            const timeoutPromise = new Promise<never>((_, reject) => {\r\n              setTimeout(() => reject(new Error('Request timeout')), opts.request_timeout);\r\n            });\r\n\r\n            // Remove article with timeout\r\n            const removePromise = this.removeArticle(accessToken, articleId);\r\n            const response = await Promise.race([removePromise, timeoutPromise]);\r\n\r\n            const processingTime = Date.now() - itemStartTime;\r\n            successfulCount++;\r\n\r\n            const result: BulkRemoveItemResult = {\r\n              article_id: articleId,\r\n              success: true,\r\n              message: response.message,\r\n              processing_time: processingTime\r\n            };\r\n\r\n            results.push(result);\r\n            return result;\r\n\r\n          } catch (error) {\r\n            const processingTime = Date.now() - itemStartTime;\r\n            failedCount++;\r\n\r\n            const errorMessage = error instanceof Error ? error.message : 'Unknown error';\r\n            const errorType = error instanceof ZaloSDKError ? 'ZaloSDKError' : 'UnknownError';\r\n\r\n            // Track error types\r\n            errorSummary[errorType] = (errorSummary[errorType] || 0) + 1;\r\n\r\n            const result: BulkRemoveItemResult = {\r\n              article_id: articleId,\r\n              success: false,\r\n              error: errorMessage,\r\n              processing_time: processingTime\r\n            };\r\n\r\n            results.push(result);\r\n\r\n            // Stop processing if continue_on_error is false\r\n            if (!opts.continue_on_error) {\r\n              throw new ZaloSDKError(\r\n                `Bulk removal stopped due to error on article ${articleId}: ${errorMessage}`,\r\n                -1\r\n              );\r\n            }\r\n\r\n            return result;\r\n          }\r\n        });\r\n\r\n        // Process batch with concurrency limit\r\n        const chunks = [];\r\n        for (let j = 0; j < batchPromises.length; j += opts.max_concurrency) {\r\n          chunks.push(batchPromises.slice(j, j + opts.max_concurrency));\r\n        }\r\n\r\n        for (const chunk of chunks) {\r\n          await Promise.all(chunk);\r\n        }\r\n\r\n        // Update progress after each batch\r\n        updateProgress(Math.min(i + opts.batch_size, validArticleIds.length));\r\n\r\n        // Add delay between batches (except for the last batch)\r\n        if (i + opts.batch_size < validArticleIds.length && opts.batch_delay > 0) {\r\n          await new Promise(resolve => setTimeout(resolve, opts.batch_delay));\r\n        }\r\n      }\r\n\r\n      // Final progress update\r\n      updateProgress(validArticleIds.length, true);\r\n\r\n      // Calculate final statistics\r\n      const totalTime = Date.now() - startTime;\r\n      const successRate = validArticleIds.length > 0\r\n        ? Math.round((successfulCount / validArticleIds.length) * 100)\r\n        : 0;\r\n\r\n      // Return complete result\r\n      return {\r\n        total_processed: validArticleIds.length,\r\n        successful_count: successfulCount,\r\n        failed_count: failedCount,\r\n        success_rate: successRate,\r\n        total_time: totalTime,\r\n        results: results.sort((a, b) =>\r\n          validArticleIds.indexOf(a.article_id) - validArticleIds.indexOf(b.article_id)\r\n        ),\r\n        error_summary: Object.keys(errorSummary).length > 0 ? errorSummary : undefined\r\n      };\r\n\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to remove articles in bulk\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Remove multiple articles in bulk with simple interface (no progress tracking)\r\n   * @param accessToken OA access token\r\n   * @param articleIds Array of article IDs to remove\r\n   * @param options Optional settings for bulk removal\r\n   * @returns Simple summary of bulk removal operation\r\n   *\r\n   * Simplified version of removeBulkArticles that returns just the summary statistics\r\n   */\r\n  async removeBulkArticlesSimple(\r\n    accessToken: string,\r\n    articleIds: string[],\r\n    options?: BulkRemoveOptions\r\n  ): Promise<{\r\n    total_processed: number;\r\n    successful_count: number;\r\n    failed_count: number;\r\n    success_rate: number;\r\n    failed_articles?: string[];\r\n  }> {\r\n    const result = await this.removeBulkArticles(accessToken, articleIds, options);\r\n\r\n    const failedArticles = result.results\r\n      .filter(r => !r.success)\r\n      .map(r => r.article_id);\r\n\r\n    return {\r\n      total_processed: result.total_processed,\r\n      successful_count: result.successful_count,\r\n      failed_count: result.failed_count,\r\n      success_rate: result.success_rate,\r\n      failed_articles: failedArticles.length > 0 ? failedArticles : undefined\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Get all articles with full details (advanced function with progress tracking)\r\n   * @param accessToken OA access token\r\n   * @param type Article type (\"normal\", \"video\", or \"both\")\r\n   * @param options Options for bulk details fetch operation\r\n   * @param progressCallback Optional callback to track progress\r\n   * @returns Complete result with all articles and their full details\r\n   *\r\n   * This function automatically:\r\n   * 1. Fetches all articles list using getAllArticles() or getAllArticlesCombined()\r\n   * 2. Fetches detailed information for each article using getArticleDetail()\r\n   * 3. Handles batch processing and concurrency control\r\n   * 4. Provides detailed progress tracking and error handling\r\n   * 5. Returns both successful and failed results with statistics\r\n   */\r\n  async getAllArticlesWithDetails(\r\n    accessToken: string,\r\n    type: \"normal\" | \"video\" | \"both\" = \"both\",\r\n    options?: BulkDetailOptions,\r\n    progressCallback?: (progress: BulkDetailProgress) => void\r\n  ): Promise<BulkDetailResult> {\r\n    try {\r\n      // Validate input\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      // Set default options\r\n      const opts: Required<BulkDetailOptions> = {\r\n        detail_batch_size: 5,\r\n        max_concurrency: 3,\r\n        continue_on_error: true,\r\n        batch_delay: 200,\r\n        request_timeout: 15000,\r\n        max_articles: 0, // 0 = unlimited\r\n        list_options: {\r\n          batch_size: 10, // Zalo API maximum limit is 10\r\n          max_articles: 0 // 0 = unlimited\r\n        },\r\n        ...options\r\n      };\r\n\r\n      // Initialize tracking variables\r\n      const results: BulkDetailItemResult[] = [];\r\n      const articlesWithDetails: ArticleDetail[] = [];\r\n      const errorSummary: { [errorType: string]: number } = {};\r\n      const startTime = Date.now();\r\n      let successfulCount = 0;\r\n      let failedCount = 0;\r\n\r\n      // Progress tracking helper\r\n      const updateProgress = (\r\n        currentCount: number,\r\n        totalCount: number,\r\n        phase: BulkDetailProgress['phase'],\r\n        isComplete: boolean = false\r\n      ) => {\r\n        if (progressCallback) {\r\n          const percentage = totalCount > 0 ? Math.round((currentCount / totalCount) * 100) : 0;\r\n          const currentBatch = phase === 'fetching_details'\r\n            ? Math.ceil(currentCount / opts.detail_batch_size)\r\n            : undefined;\r\n          const totalBatches = phase === 'fetching_details'\r\n            ? Math.ceil(totalCount / opts.detail_batch_size)\r\n            : undefined;\r\n\r\n          progressCallback({\r\n            current_count: currentCount,\r\n            total_count: totalCount,\r\n            percentage,\r\n            successful_count: successfulCount,\r\n            failed_count: failedCount,\r\n            is_complete: isComplete,\r\n            phase,\r\n            current_batch: currentBatch,\r\n            total_batches: totalBatches\r\n          });\r\n        }\r\n      };\r\n\r\n      // Phase 1: Fetch articles list\r\n      updateProgress(0, 0, 'fetching_list');\r\n\r\n      const listStartTime = Date.now();\r\n      let articlesList: ArticleListItem[] = [];\r\n      let listFetchStats = { total_pages_fetched: 0, list_fetch_time: 0 };\r\n      const listMaxArticles = opts.list_options.max_articles ?? 500;\r\n\r\n      if (type === \"both\") {\r\n        const combinedResult = await this.getAllArticlesCombined(accessToken, {\r\n          batchSize: opts.list_options.batch_size,\r\n          maxArticlesPerType: listMaxArticles,\r\n          onProgress: (progress) => {\r\n            // Convert combined progress to simple progress for our callback\r\n            updateProgress(progress.totalFetched, progress.totalFetched, 'fetching_list');\r\n          }\r\n        });\r\n\r\n        articlesList = combinedResult.articles;\r\n        listFetchStats.total_pages_fetched = combinedResult.totalBatches;\r\n\r\n      } else {\r\n        const singleResult = await this.getAllArticles(accessToken, type, {\r\n          batchSize: opts.list_options.batch_size,\r\n          maxArticles: listMaxArticles,\r\n          onProgress: (progress) => {\r\n            updateProgress(progress.totalFetched, progress.totalFetched, 'fetching_list');\r\n          }\r\n        });\r\n\r\n        articlesList = singleResult.articles;\r\n        listFetchStats.total_pages_fetched = singleResult.totalBatches;\r\n      }\r\n\r\n      listFetchStats.list_fetch_time = Date.now() - listStartTime;\r\n\r\n      // Apply max_articles limit if specified\r\n      if (opts.max_articles > 0 && articlesList.length > opts.max_articles) {\r\n        articlesList = articlesList.slice(0, opts.max_articles);\r\n      }\r\n\r\n      const totalArticles = articlesList.length;\r\n\r\n      if (totalArticles === 0) {\r\n        updateProgress(0, 0, 'complete', true);\r\n        return {\r\n          total_processed: 0,\r\n          successful_count: 0,\r\n          failed_count: 0,\r\n          success_rate: 0,\r\n          total_time: Date.now() - startTime,\r\n          results: [],\r\n          articles_with_details: [],\r\n          list_fetch_stats: listFetchStats\r\n        };\r\n      }\r\n\r\n      // Phase 2: Fetch details for each article\r\n      updateProgress(0, totalArticles, 'fetching_details');\r\n\r\n      // Process articles in batches\r\n      for (let i = 0; i < totalArticles; i += opts.detail_batch_size) {\r\n        const batch = articlesList.slice(i, i + opts.detail_batch_size);\r\n\r\n        // Create promises for current batch with concurrency limit\r\n        const batchPromises = batch.map(async (article) => {\r\n          const itemStartTime = Date.now();\r\n\r\n          try {\r\n            // Create timeout promise\r\n            const timeoutPromise = new Promise<never>((_, reject) => {\r\n              setTimeout(() => reject(new Error('Request timeout')), opts.request_timeout);\r\n            });\r\n\r\n            // Fetch article detail with timeout\r\n            const detailPromise = this.getArticleDetail(accessToken, article.id);\r\n            const detail = await Promise.race([detailPromise, timeoutPromise]);\r\n\r\n            const processingTime = Date.now() - itemStartTime;\r\n            successfulCount++;\r\n\r\n            const result: BulkDetailItemResult = {\r\n              article_id: article.id,\r\n              success: true,\r\n              detail: detail,\r\n              processing_time: processingTime\r\n            };\r\n\r\n            results.push(result);\r\n            articlesWithDetails.push(detail);\r\n            return result;\r\n\r\n          } catch (error) {\r\n            const processingTime = Date.now() - itemStartTime;\r\n            failedCount++;\r\n\r\n            const errorMessage = error instanceof Error ? error.message : 'Unknown error';\r\n            const errorType = error instanceof ZaloSDKError ? 'ZaloSDKError' : 'UnknownError';\r\n\r\n            // Track error types\r\n            errorSummary[errorType] = (errorSummary[errorType] || 0) + 1;\r\n\r\n            const result: BulkDetailItemResult = {\r\n              article_id: article.id,\r\n              success: false,\r\n              error: errorMessage,\r\n              processing_time: processingTime\r\n            };\r\n\r\n            results.push(result);\r\n\r\n            // Stop processing if continue_on_error is false\r\n            if (!opts.continue_on_error) {\r\n              throw new ZaloSDKError(\r\n                `Bulk details fetch stopped due to error on article ${article.id}: ${errorMessage}`,\r\n                -1\r\n              );\r\n            }\r\n\r\n            return result;\r\n          }\r\n        });\r\n\r\n        // Process batch with concurrency limit\r\n        const chunks = [];\r\n        for (let j = 0; j < batchPromises.length; j += opts.max_concurrency) {\r\n          chunks.push(batchPromises.slice(j, j + opts.max_concurrency));\r\n        }\r\n\r\n        for (const chunk of chunks) {\r\n          await Promise.all(chunk);\r\n        }\r\n\r\n        // Update progress after each batch\r\n        updateProgress(Math.min(i + opts.detail_batch_size, totalArticles), totalArticles, 'fetching_details');\r\n\r\n        // Add delay between batches (except for the last batch)\r\n        if (i + opts.detail_batch_size < totalArticles && opts.batch_delay > 0) {\r\n          await new Promise(resolve => setTimeout(resolve, opts.batch_delay));\r\n        }\r\n      }\r\n\r\n      // Final progress update\r\n      updateProgress(totalArticles, totalArticles, 'complete', true);\r\n\r\n      // Calculate final statistics\r\n      const totalTime = Date.now() - startTime;\r\n      const successRate = totalArticles > 0\r\n        ? Math.round((successfulCount / totalArticles) * 100)\r\n        : 0;\r\n\r\n      // Return complete result\r\n      return {\r\n        total_processed: totalArticles,\r\n        successful_count: successfulCount,\r\n        failed_count: failedCount,\r\n        success_rate: successRate,\r\n        total_time: totalTime,\r\n        results: results.sort((a, b) =>\r\n          articlesList.findIndex(art => art.id === a.article_id) -\r\n          articlesList.findIndex(art => art.id === b.article_id)\r\n        ),\r\n        articles_with_details: articlesWithDetails,\r\n        error_summary: Object.keys(errorSummary).length > 0 ? errorSummary : undefined,\r\n        list_fetch_stats: listFetchStats\r\n      };\r\n\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to get all articles with details\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get all articles with full details - Simple interface (no progress tracking)\r\n   * @param accessToken OA access token\r\n   * @param type Article type (\"normal\", \"video\", or \"both\")\r\n   * @param options Optional settings for details fetch\r\n   * @returns Array of articles with full details\r\n   *\r\n   * Simplified version that returns just the articles with details array\r\n   */\r\n  async getAllArticlesWithDetailsSimple(\r\n    accessToken: string,\r\n    type: \"normal\" | \"video\" | \"both\" = \"both\",\r\n    options?: BulkDetailOptions\r\n  ): Promise<ArticleDetail[]> {\r\n    const result = await this.getAllArticlesWithDetails(accessToken, type, options);\r\n    return result.articles_with_details;\r\n  }\r\n\r\n  /**\r\n   * Create article and wait for completion - All-in-one method\r\n   * @param accessToken OA access token\r\n   * @param request Article information\r\n   * @param options Options for tracking and timeout\r\n   * @param progressCallback Optional callback to track progress\r\n   * @returns Article ID when creation is complete\r\n   *\r\n   * This method combines create + tracking + verification into one call:\r\n   * 1. Creates the article and gets token\r\n   * 2. Automatically tracks progress until completion\r\n   * 3. Returns the final article ID\r\n   * 4. Handles all errors and timeouts gracefully\r\n   */\r\n  async createArticleAndWaitForCompletion(\r\n    accessToken: string,\r\n    request: ArticleRequest,\r\n    options?: {\r\n      /** Maximum time to wait for completion in milliseconds (default: 60000 = 1 minute) */\r\n      timeout?: number;\r\n      /** Interval between progress checks in milliseconds (default: 2000 = 2 seconds) */\r\n      checkInterval?: number;\r\n      /** Whether to return article details instead of just ID (default: false) */\r\n      returnDetails?: boolean;\r\n    },\r\n    progressCallback?: (progress: {\r\n      phase: 'creating' | 'processing' | 'verifying' | 'complete';\r\n      message: string;\r\n      token?: string;\r\n      article_id?: string;\r\n      elapsed_time: number;\r\n    }) => void\r\n  ): Promise<{\r\n    article_id: string;\r\n    token: string;\r\n    total_time: number;\r\n    article_details?: ArticleDetail;\r\n  }> {\r\n    try {\r\n      // Validate input\r\n      if (!accessToken || accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token cannot be empty\", -1);\r\n      }\r\n\r\n      if (!request) {\r\n        throw new ZaloSDKError(\"Article request cannot be empty\", -1);\r\n      }\r\n\r\n      // Set default options\r\n      const opts = {\r\n        timeout: 60000, // 1 minute\r\n        checkInterval: 2000, // 2 seconds\r\n        returnDetails: false,\r\n        ...options\r\n      };\r\n\r\n      const startTime = Date.now();\r\n      let token: string;\r\n      let articleId: string | undefined;\r\n\r\n      // Progress helper\r\n      const updateProgress = (\r\n        phase: 'creating' | 'processing' | 'verifying' | 'complete',\r\n        message: string,\r\n        additionalData?: { token?: string; article_id?: string }\r\n      ) => {\r\n        if (progressCallback) {\r\n          progressCallback({\r\n            phase,\r\n            message,\r\n            token: additionalData?.token || token,\r\n            article_id: additionalData?.article_id || articleId,\r\n            elapsed_time: Date.now() - startTime\r\n          });\r\n        }\r\n      };\r\n\r\n      // Phase 1: Create article\r\n      updateProgress('creating', 'Creating article...');\r\n\r\n      const createResponse = await this.createArticle(accessToken, request);\r\n      token = createResponse.data?.token as string;\r\n\r\n      if (!token) {\r\n        throw new ZaloSDKError(\"No token received from article creation\", -1);\r\n      }\r\n\r\n      updateProgress('processing', 'Article created, waiting for processing...', { token });\r\n\r\n      // Phase 2: Track progress until completion\r\n      const maxAttempts = Math.ceil(opts.timeout / opts.checkInterval);\r\n      let attempts = 0;\r\n      let isComplete = false;\r\n\r\n      while (attempts < maxAttempts && !isComplete) {\r\n        attempts++;\r\n\r\n        try {\r\n          // Check if timeout exceeded\r\n          const elapsedTime = Date.now() - startTime;\r\n          if (elapsedTime >= opts.timeout) {\r\n            throw new ZaloSDKError(\r\n              `Article creation timeout after ${Math.round(elapsedTime / 1000)} seconds. Token: ${token}`,\r\n              -1\r\n            );\r\n          }\r\n\r\n          updateProgress('processing', `Checking progress... (attempt ${attempts}/${maxAttempts})`);\r\n\r\n          // Try to verify article (this will throw if still processing)\r\n          const verifyResult = await this.verifyArticle(accessToken, token);\r\n\r\n          if (verifyResult && verifyResult.id) {\r\n            articleId = verifyResult.id;\r\n            isComplete = true;\r\n            updateProgress('verifying', 'Article processing completed!', { article_id: articleId });\r\n            break;\r\n          }\r\n\r\n        } catch (error) {\r\n          const elapsedTime = Date.now() - startTime;\r\n\r\n          // Handle expected errors during processing\r\n          if (error instanceof ZaloSDKError) {\r\n            if (error.message.includes(\"token was expired\")) {\r\n              // Token expired usually means processing is done, but we need to find the article\r\n              updateProgress('verifying', 'Token expired, attempting to find article...');\r\n\r\n              // Try to find the article by checking recent articles\r\n              try {\r\n                const recentArticles: ArticleListResponse = await this.getArticleList(accessToken, {\r\n                  offset: 0,\r\n                  limit: 10,\r\n                  type: request.type\r\n                });\r\n\r\n                // Look for article created around the same time (within last 5 minutes)\r\n                // ArticleListResponse có thể là ZaloResponse<ArticleListData> hoặc error response\r\n                if ('data' in recentArticles && recentArticles.data?.medias) {\r\n                  const recentArticle = recentArticles.data.medias.find((article: ArticleListItem) => {\r\n                    const articleTime = new Date(article.create_date).getTime();\r\n                    const timeDiff = Math.abs(articleTime - startTime);\r\n                    return timeDiff < 5 * 60 * 1000 && article.title === request.title;\r\n                  });\r\n\r\n                  if (recentArticle) {\r\n                    articleId = recentArticle.id;\r\n                    isComplete = true;\r\n                    updateProgress('complete', 'Article found after token expiration!', { article_id: articleId });\r\n                    break;\r\n                  }\r\n                }\r\n              } catch (searchError) {\r\n                // Continue with normal timeout handling\r\n              }\r\n            }\r\n\r\n            // For other API errors, continue waiting if we haven't timed out\r\n            if (elapsedTime < opts.timeout) {\r\n              updateProgress('processing', `Still processing... (${Math.round(elapsedTime / 1000)}s elapsed)`);\r\n              await new Promise(resolve => setTimeout(resolve, opts.checkInterval));\r\n              continue;\r\n            }\r\n          }\r\n\r\n          // If we've reached here and timed out, throw the error\r\n          if (elapsedTime >= opts.timeout) {\r\n            throw new ZaloSDKError(\r\n              `Article creation timeout after ${Math.round(elapsedTime / 1000)} seconds. Last error: ${error instanceof Error ? error.message : 'Unknown error'}. Token: ${token}`,\r\n              -1\r\n            );\r\n          }\r\n\r\n          // For unexpected errors, wait and retry\r\n          updateProgress('processing', `Retrying after error... (${error instanceof Error ? error.message : 'Unknown error'})`);\r\n          await new Promise(resolve => setTimeout(resolve, opts.checkInterval));\r\n        }\r\n      }\r\n\r\n      // Final check if we didn't get article ID\r\n      if (!articleId) {\r\n        throw new ZaloSDKError(\r\n          `Failed to get article ID after ${attempts} attempts. Token: ${token}`,\r\n          -1\r\n        );\r\n      }\r\n\r\n      const totalTime = Date.now() - startTime;\r\n      updateProgress('complete', `Article created successfully! ID: ${articleId}`, { article_id: articleId });\r\n\r\n      // Phase 3: Get article details if requested\r\n      let articleDetails: ArticleDetail | undefined;\r\n      if (opts.returnDetails) {\r\n        try {\r\n          updateProgress('complete', 'Fetching article details...');\r\n          articleDetails = await this.getArticleDetail(accessToken, articleId);\r\n        } catch (error) {\r\n          // Don't fail the whole operation if we can't get details\r\n          console.warn('Failed to fetch article details:', error);\r\n        }\r\n      }\r\n\r\n      return {\r\n        article_id: articleId,\r\n        token: token,\r\n        total_time: totalTime,\r\n        article_details: articleDetails\r\n      };\r\n\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to create article and wait for completion\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Create article and wait for completion - Simple interface\r\n   * @param accessToken OA access token\r\n   * @param request Article information\r\n   * @param timeoutMs Optional timeout in milliseconds (default: 60000)\r\n   * @returns Article ID when creation is complete\r\n   *\r\n   * Simplified version that returns just the article ID\r\n   */\r\n  async createArticleAndGetId(\r\n    accessToken: string,\r\n    request: ArticleRequest,\r\n    timeoutMs: number = 60000\r\n  ): Promise<string> {\r\n    const result = await this.createArticleAndWaitForCompletion(\r\n      accessToken,\r\n      request,\r\n      { timeout: timeoutMs }\r\n    );\r\n    return result.article_id;\r\n  }\r\n\r\n  private handleArticleError(error: any, defaultMessage: string): Error {\r\n    if (error instanceof ZaloSDKError) {\r\n      return error;\r\n    }\r\n    if (error.response?.data) {\r\n      const errorData = error.response.data;\r\n      return new ZaloSDKError(\r\n        `${defaultMessage}: ${errorData.message || errorData.error || \"Unknown error\"\r\n        }`,\r\n        errorData.error || -1,\r\n        errorData\r\n      );\r\n    }\r\n    return new ZaloSDKError(\r\n      `${defaultMessage}: ${error.message || \"Unknown error\"}`,\r\n      -1,\r\n      error\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Update normal article\r\n   * @param accessToken OA access token\r\n   * @param request Update information\r\n   * @returns Token for tracking update progress\r\n   */\r\n  async updateNormalArticle(\r\n    accessToken: string,\r\n    request: ArticleUpdateNormalRequest\r\n  ): Promise<ArticleUpdateResponse> {\r\n    try {\r\n      // Validate input\r\n      this.validateUpdateNormalArticleRequest(request);\r\n\r\n      // Process tracking link\r\n      let processedTrackingLink = \"https://v2.redai.vn/auth\";\r\n      if (\r\n        request.tracking_link &&\r\n        this.isValidTrackingLink(request.tracking_link)\r\n      ) {\r\n        processedTrackingLink = request.tracking_link;\r\n      }\r\n\r\n      const data = {\r\n        id: request.id,\r\n        type: request.type,\r\n        title: request.title,\r\n        author: request.author,\r\n        cover: request.cover,\r\n        description: request.description,\r\n        body: request.body,\r\n        related_medias: request.related_medias || [],\r\n        tracking_link: processedTrackingLink,\r\n        status: request.status || ArticleStatus.HIDE,\r\n        comment: request.comment || CommentStatus.SHOW,\r\n      };\r\n\r\n      const response = await this.client.apiPost<ArticleUpdateResponse>(\r\n        this.endpoints.article.update,\r\n        accessToken,\r\n        data\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to update normal article\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Update video article\r\n   * @param accessToken OA access token\r\n   * @param request Update information\r\n   * @returns Token for tracking update progress\r\n   */\r\n  async updateVideoArticle(\r\n    accessToken: string,\r\n    request: ArticleUpdateVideoRequest\r\n  ): Promise<ArticleUpdateResponse> {\r\n    try {\r\n      // Validate input\r\n      this.validateUpdateVideoArticleRequest(request);\r\n\r\n      const data = {\r\n        id: request.id,\r\n        type: request.type,\r\n        title: request.title,\r\n        description: request.description,\r\n        video_id: request.video_id,\r\n        avatar: request.avatar,\r\n        status: request.status || ArticleStatus.HIDE,\r\n        comment: request.comment || CommentStatus.SHOW,\r\n      };\r\n\r\n      const response = await this.client.apiPost<ArticleUpdateResponse>(\r\n        this.endpoints.article.update,\r\n        accessToken,\r\n        data\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to update video article\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Update article (automatically detect type)\r\n   * @param accessToken OA access token\r\n   * @param request Update information\r\n   * @returns Token for tracking update progress\r\n   */\r\n  async updateArticle(\r\n    accessToken: string,\r\n    request: ArticleUpdateRequest\r\n  ): Promise<ArticleUpdateResponse> {\r\n    if (request.type === ArticleType.NORMAL) {\r\n      return this.updateNormalArticle(accessToken, request);\r\n    } else if (request.type === ArticleType.VIDEO) {\r\n      return this.updateVideoArticle(accessToken, request);\r\n    } else {\r\n      throw new ZaloSDKError(\r\n        'Invalid article type. Only \"normal\" and \"video\" are supported',\r\n        -1\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Update article status only\r\n   * @param accessToken OA access token\r\n   * @param articleId Article ID to update\r\n   * @param status New status ('show' or 'hide')\r\n   * @param comment Optional comment status ('show' or 'hide')\r\n   * @returns Token for tracking update progress\r\n   */\r\n  async updateArticleStatus(\r\n    accessToken: string,\r\n    articleId: string,\r\n    status: ArticleStatus,\r\n    comment?: CommentStatus\r\n  ): Promise<ArticleUpdateResponse> {\r\n    try {\r\n      // Validate input\r\n      if (!articleId || articleId.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Article ID cannot be empty\", -1);\r\n      }\r\n\r\n      if (!Object.values(ArticleStatus).includes(status)) {\r\n        throw new ZaloSDKError(\r\n          `Invalid status. Must be one of: ${Object.values(ArticleStatus).join(', ')}`,\r\n          -1\r\n        );\r\n      }\r\n\r\n      if (comment && !Object.values(CommentStatus).includes(comment)) {\r\n        throw new ZaloSDKError(\r\n          `Invalid comment status. Must be one of: ${Object.values(CommentStatus).join(', ')}`,\r\n          -1\r\n        );\r\n      }\r\n\r\n      // 1. Get article details first\r\n      const articleDetail = await this.getArticleDetail(accessToken, articleId);\r\n\r\n      // 2. Prepare update request based on article type\r\n      let updateRequest: ArticleUpdateRequest;\r\n\r\n      if (articleDetail.type === ArticleType.NORMAL) {\r\n        // For normal articles\r\n        const normalDetail = articleDetail as ArticleDetailNormal;\r\n        updateRequest = {\r\n          id: articleId,\r\n          type: ArticleType.NORMAL,\r\n          title: normalDetail.title,\r\n          author: normalDetail.author,\r\n          cover: normalDetail.cover,\r\n          description: normalDetail.description,\r\n          body: normalDetail.body,\r\n          related_medias: normalDetail.related_medias,\r\n          tracking_link: normalDetail.tracking_link,\r\n          status: status,\r\n          comment: comment || normalDetail.comment,\r\n        } as ArticleUpdateNormalRequest;\r\n      } else if (articleDetail.type === ArticleType.VIDEO) {\r\n        // For video articles\r\n        const videoDetail = articleDetail as ArticleDetailVideo;\r\n        updateRequest = {\r\n          id: articleId,\r\n          type: ArticleType.VIDEO,\r\n          title: videoDetail.title,\r\n          description: videoDetail.description,\r\n          video_id: videoDetail.video_id,\r\n          avatar: videoDetail.avatar,\r\n          status: status,\r\n          comment: comment || videoDetail.comment,\r\n        } as ArticleUpdateVideoRequest;\r\n      } else {\r\n        throw new ZaloSDKError(\r\n          `Unsupported article type: ${(articleDetail as any).type}`,\r\n          -1\r\n        );\r\n      }\r\n\r\n      // 3. Update the article with new status\r\n      return await this.updateArticle(accessToken, updateRequest);\r\n    } catch (error) {\r\n      throw this.handleArticleError(error, \"Failed to update article status\");\r\n    }\r\n  }\r\n\r\n  // ==================== VALIDATION METHODS ====================\r\n\r\n  /**\r\n   * Validate article request (universal method)\r\n   * @param request Article creation request\r\n   */\r\n  public validateArticleRequest(request: ArticleRequest): void {\r\n    if (!request) {\r\n      throw new ZaloSDKError(\"Request cannot be empty\", -1);\r\n    }\r\n\r\n    if (request.type === ArticleType.NORMAL) {\r\n      this.validateNormalArticleRequest(request);\r\n    } else if (request.type === ArticleType.VIDEO) {\r\n      this.validateVideoArticleRequest(request);\r\n    } else {\r\n      throw new ZaloSDKError(\r\n        'Invalid article type. Only \"normal\" and \"video\" are supported',\r\n        -1\r\n      );\r\n    }\r\n  }\r\n\r\n\r\n  /**\r\n   * Validate normal article creation request\r\n   */\r\n  private validateNormalArticleRequest(request: ArticleNormalRequest): void {\r\n    if (!request.title || request.title.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Title cannot be empty\", -1);\r\n    }\r\n\r\n    if (request.title.length > ARTICLE_CONSTRAINTS.TITLE_MAX_LENGTH) {\r\n      throw new ZaloSDKError(\r\n        `Title cannot exceed ${ARTICLE_CONSTRAINTS.TITLE_MAX_LENGTH} characters`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (!request.author || request.author.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Author cannot be empty\", -1);\r\n    }\r\n\r\n    if (request.author.length > ARTICLE_CONSTRAINTS.AUTHOR_MAX_LENGTH) {\r\n      throw new ZaloSDKError(\r\n        `Author name cannot exceed ${ARTICLE_CONSTRAINTS.AUTHOR_MAX_LENGTH} characters`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (!request.description || request.description.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Description cannot be empty\", -1);\r\n    }\r\n\r\n    if (\r\n      request.description.length > ARTICLE_CONSTRAINTS.DESCRIPTION_MAX_LENGTH\r\n    ) {\r\n      throw new ZaloSDKError(\r\n        `Description cannot exceed ${ARTICLE_CONSTRAINTS.DESCRIPTION_MAX_LENGTH} characters`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (!request.cover) {\r\n      throw new ZaloSDKError(\"Cover information cannot be empty\", -1);\r\n    }\r\n\r\n    if (!request.body || request.body.length === 0) {\r\n      throw new ZaloSDKError(\"Article body cannot be empty\", -1);\r\n    }\r\n\r\n    // Validate cover\r\n    this.validateCover(request.cover);\r\n\r\n    // Validate body items\r\n    request.body.forEach((item, index) => {\r\n      this.validateBodyItem(item, index);\r\n    });\r\n\r\n    // Validate optional fields\r\n    if (request.status && ![\"show\", \"hide\"].includes(request.status)) {\r\n      throw new ZaloSDKError('Status must be \"show\" or \"hide\"', -1);\r\n    }\r\n\r\n    if (request.comment && ![\"show\", \"hide\"].includes(request.comment)) {\r\n      throw new ZaloSDKError('Comment status must be \"show\" or \"hide\"', -1);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate video article creation request\r\n   */\r\n  private validateVideoArticleRequest(request: ArticleVideoRequest): void {\r\n    if (!request.title || request.title.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Title cannot be empty\", -1);\r\n    }\r\n\r\n    if (request.title.length > ARTICLE_CONSTRAINTS.TITLE_MAX_LENGTH) {\r\n      throw new ZaloSDKError(\r\n        `Title cannot exceed ${ARTICLE_CONSTRAINTS.TITLE_MAX_LENGTH} characters`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (!request.description || request.description.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Description cannot be empty\", -1);\r\n    }\r\n\r\n    if (\r\n      request.description.length > ARTICLE_CONSTRAINTS.DESCRIPTION_MAX_LENGTH\r\n    ) {\r\n      throw new ZaloSDKError(\r\n        `Description cannot exceed ${ARTICLE_CONSTRAINTS.DESCRIPTION_MAX_LENGTH} characters`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (!request.video_id || request.video_id.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Video ID cannot be empty\", -1);\r\n    }\r\n\r\n    if (!request.avatar || request.avatar.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Avatar URL cannot be empty\", -1);\r\n    }\r\n\r\n    // Validate optional fields\r\n    if (request.status && ![\"show\", \"hide\"].includes(request.status)) {\r\n      throw new ZaloSDKError('Status must be \"show\" or \"hide\"', -1);\r\n    }\r\n\r\n    if (request.comment && ![\"show\", \"hide\"].includes(request.comment)) {\r\n      throw new ZaloSDKError('Comment status must be \"show\" or \"hide\"', -1);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate article list request\r\n   */\r\n  private validateArticleListRequest(request: ArticleListRequest): void {\r\n    if (request.offset < 0) {\r\n      throw new ZaloSDKError(\"Offset must be >= 0\", -1);\r\n    }\r\n\r\n    if (request.limit <= 0) {\r\n      throw new ZaloSDKError(\"Limit must be > 0\", -1);\r\n    }\r\n\r\n    if (request.limit > ARTICLE_CONSTRAINTS.LIST_MAX_LIMIT) {\r\n      throw new ZaloSDKError(\r\n        `Limit cannot exceed ${ARTICLE_CONSTRAINTS.LIST_MAX_LIMIT}`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (![\"normal\", \"video\"].includes(request.type)) {\r\n      throw new ZaloSDKError('Type must be \"normal\" or \"video\"', -1);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate normal article update request\r\n   */\r\n  private validateUpdateNormalArticleRequest(\r\n    request: ArticleUpdateNormalRequest\r\n  ): void {\r\n    if (!request.id || request.id.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Article ID cannot be empty\", -1);\r\n    }\r\n\r\n    // Use the same validation as creation\r\n    this.validateNormalArticleRequest(request);\r\n  }\r\n\r\n  /**\r\n   * Validate video article update request\r\n   */\r\n  private validateUpdateVideoArticleRequest(\r\n    request: ArticleUpdateVideoRequest\r\n  ): void {\r\n    if (!request.id || request.id.trim() === \"\") {\r\n      throw new ZaloSDKError(\"Article ID cannot be empty\", -1);\r\n    }\r\n\r\n    // Use the same validation as creation\r\n    this.validateVideoArticleRequest(request);\r\n  }\r\n\r\n  /**\r\n   * Validate cover information\r\n   */\r\n  private validateCover(cover: ArticleCover): void {\r\n    if (!cover.cover_type) {\r\n      throw new ZaloSDKError(\"Cover type cannot be empty\", -1);\r\n    }\r\n\r\n    if (![\"photo\", \"video\"].includes(cover.cover_type)) {\r\n      throw new ZaloSDKError('Cover type must be \"photo\" or \"video\"', -1);\r\n    }\r\n\r\n    if (\r\n      cover.cover_type === CoverType.PHOTO &&\r\n      (!cover.photo_url || cover.photo_url.trim() === \"\")\r\n    ) {\r\n      throw new ZaloSDKError(\r\n        'Photo URL cannot be empty when cover_type is \"photo\"',\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (cover.cover_type === CoverType.VIDEO) {\r\n      if (!cover.video_id || cover.video_id.trim() === \"\") {\r\n        throw new ZaloSDKError(\r\n          'Video ID cannot be empty when cover_type is \"video\"',\r\n          -1\r\n        );\r\n      }\r\n\r\n      if (\r\n        cover.cover_view &&\r\n        ![\"horizontal\", \"vertical\", \"square\"].includes(cover.cover_view)\r\n      ) {\r\n        throw new ZaloSDKError(\r\n          'cover_view must be \"horizontal\", \"vertical\" or \"square\"',\r\n          -1\r\n        );\r\n      }\r\n    }\r\n\r\n    if (![\"show\", \"hide\"].includes(cover.status)) {\r\n      throw new ZaloSDKError('Cover status must be \"show\" or \"hide\"', -1);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate body item\r\n   */\r\n  private validateBodyItem(item: ArticleBodyItem, index: number): void {\r\n    if (!item.type) {\r\n      throw new ZaloSDKError(\r\n        `Content type at position ${index + 1} cannot be empty`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (![\"text\", \"image\", \"video\", \"product\"].includes(item.type)) {\r\n      throw new ZaloSDKError(\r\n        `Content type at position ${index + 1\r\n        } must be \"text\", \"image\", \"video\" or \"product\"`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    switch (item.type) {\r\n      case BodyItemType.TEXT:\r\n        if (!item.content || item.content.trim() === \"\") {\r\n          throw new ZaloSDKError(\r\n            `Text content at position ${index + 1} cannot be empty`,\r\n            -1\r\n          );\r\n        }\r\n        break;\r\n\r\n      case BodyItemType.IMAGE:\r\n        if (!item.url || item.url.trim() === \"\") {\r\n          throw new ZaloSDKError(\r\n            `Image URL at position ${index + 1} cannot be empty`,\r\n            -1\r\n          );\r\n        }\r\n        break;\r\n\r\n      case BodyItemType.VIDEO:\r\n        if (!item.video_id && !item.url) {\r\n          throw new ZaloSDKError(\r\n            `Video at position ${index + 1} must have video_id or url`,\r\n            -1\r\n          );\r\n        }\r\n        break;\r\n\r\n      case BodyItemType.PRODUCT:\r\n        if (!item.id || item.id.trim() === \"\") {\r\n          throw new ZaloSDKError(\r\n            `Product ID at position ${index + 1} cannot be empty`,\r\n            -1\r\n          );\r\n        }\r\n        break;\r\n    }\r\n  }\r\n\r\n  private isValidTrackingLink(url: string): boolean {\r\n    try {\r\n      new URL(url);\r\n      return true;\r\n    } catch {\r\n      return false;\r\n    }\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  VideoUploadResponse,\r\n  VideoStatusResponse,\r\n  VIDEO_CONSTRAINTS,\r\n  VideoUploadStatus,\r\n} from \"../types/article\";\r\nimport { ZaloSDKError } from \"../types/common\";\r\nimport axios from \"axios\";\r\nimport FormData from \"form-data\";\r\n\r\n/**\r\n * Service for handling Zalo Official Account Video Upload APIs\r\n *\r\n * CONDITIONS FOR USING ZALO VIDEO UPLOAD:\r\n *\r\n * 1. GENERAL CONDITIONS:\r\n *    - OA must have permission to upload videos\r\n *    - Access token must have \"manage_article\" scope\r\n *    - OA must have active status and be verified\r\n *\r\n * 2. VIDEO FILE REQUIREMENTS:\r\n *    - File formats: MP4, AVI only\r\n *    - Maximum file size: 50MB\r\n *    - Video processing: asynchronous, use token to check status\r\n *    - Processing time: varies based on video size and quality\r\n *\r\n * 3. UPLOAD PROCESS:\r\n *    - Step 1: Upload video file and get token\r\n *    - Step 2: Use token to check processing status\r\n *    - Step 3: Get video_id when processing is complete\r\n *    - Step 4: Use video_id in article creation\r\n *\r\n * 4. STATUS CODES:\r\n *    - 0: Trạng thái không xác định\r\n *    - 1: Video đã được xử lý thành công và có thể sử dụng\r\n *    - 2: Video đã bị khóa\r\n *    - 3: Video đang được xử lý\r\n *    - 4: Video xử lý thất bại\r\n *    - 5: Video đã bị xóa\r\n */\r\nexport class VideoUploadService {\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    video: {\r\n      upload: \"https://openapi.zalo.me/v2.0/article/upload_video/preparevideo\",\r\n      verify: \"https://openapi.zalo.me/v2.0/article/upload_video/verify\",\r\n      info: \"https://openapi.zalo.me/v2.0/article/video/info\",\r\n    },\r\n  } as const;\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Upload video file for article\r\n   * @param accessToken OA access token\r\n   * @param file Video file (Buffer or File)\r\n   * @param filename Original filename (optional, will be generated if not provided)\r\n   * @returns Token for tracking upload progress\r\n   */\r\n  async uploadVideo(\r\n    accessToken: string,\r\n    file: Buffer | File,\r\n    filename?: string\r\n  ): Promise<VideoUploadResponse> {\r\n    try {\r\n      // Validate file\r\n      this.validateVideoFile(file, filename);\r\n\r\n      // Prepare filename\r\n      const actualFilename = Buffer.isBuffer(file)\r\n        ? filename || \"video.mp4\"\r\n        : (file as File).name || filename || \"video.mp4\";\r\n\r\n      // Convert file to Buffer if needed\r\n      let bufferToUpload: Buffer;\r\n      if (Buffer.isBuffer(file)) {\r\n        bufferToUpload = file as Buffer;\r\n      } else {\r\n        // Convert File (browser-like) to Buffer for Node upload\r\n        const arrayBuffer = await (file as unknown as { arrayBuffer: () => Promise<ArrayBuffer> }).arrayBuffer();\r\n        bufferToUpload = Buffer.from(arrayBuffer);\r\n      }\r\n\r\n      // Use direct axios call with FormData (same as successful direct upload)\r\n      const formData = new FormData();\r\n      formData.append('file', bufferToUpload, {\r\n        filename: actualFilename,\r\n        contentType: this.getMimeTypeFromFilename(actualFilename),\r\n      });\r\n\r\n      const response = await axios.post(this.endpoints.video.upload, formData, {\r\n        headers: {\r\n          ...formData.getHeaders(),\r\n          'access_token': accessToken,\r\n        },\r\n        timeout: 300000, // 5 minutes timeout\r\n        maxContentLength: Infinity,\r\n        maxBodyLength: Infinity,\r\n      });\r\n\r\n      // Parse response according to Zalo API format\r\n      const responseData = response.data;\r\n\r\n      if (responseData.error && responseData.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          `Zalo API error: ${responseData.message || 'Unknown error'}`,\r\n          responseData.error\r\n        );\r\n      }\r\n\r\n      return responseData.data;\r\n    } catch (error) {\r\n      throw this.handleVideoUploadError(error, \"Failed to upload video\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Check video upload status\r\n   * @param accessToken OA access token\r\n   * @param token Token from video upload response\r\n   * @returns Video status information\r\n   */\r\n  async checkVideoStatus(\r\n    accessToken: string,\r\n    token: string\r\n  ): Promise<VideoStatusResponse> {\r\n    try {\r\n      if (!token || token.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Token cannot be empty\", -1);\r\n      }\r\n\r\n      // According to docs, token should be in header, not query params\r\n      const response = await this.client.apiGetWithHeaders<{ data: VideoStatusResponse }>(\r\n        this.endpoints.video.verify,\r\n        {\r\n          access_token: accessToken,\r\n          token: token,\r\n        }\r\n      );\r\n\r\n      return response.data;\r\n    } catch (error) {\r\n      throw this.handleVideoUploadError(error, \"Failed to check video status\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Wait for video upload completion with polling\r\n   * @param accessToken OA access token\r\n   * @param token Token from video upload response\r\n   * @param maxWaitTime Maximum wait time in milliseconds (default: 5 minutes)\r\n   * @param pollInterval Polling interval in milliseconds (default: 5 seconds)\r\n   * @returns Final video status when completed\r\n   */\r\n  async waitForUploadCompletion(\r\n    accessToken: string,\r\n    token: string,\r\n    maxWaitTime: number = 5 * 60 * 1000, // 5 minutes\r\n    pollInterval: number = 5 * 1000 // 5 seconds\r\n  ): Promise<VideoStatusResponse> {\r\n    const startTime = Date.now();\r\n\r\n    while (Date.now() - startTime < maxWaitTime) {\r\n      try {\r\n        const status = await this.checkVideoStatus(accessToken, token);\r\n\r\n        // Status 1 = Video đã được xử lý thành công và có thể sử dụng\r\n        if (status.status === VideoUploadStatus.SUCCESS && status.video_id) {\r\n          return status;\r\n        }\r\n\r\n        // Status 4 = Video xử lý thất bại\r\n        if (status.status === VideoUploadStatus.FAILED) {\r\n          throw new ZaloSDKError(\r\n            `Video processing failed: ${status.status_message}`,\r\n            status.convert_error_code\r\n          );\r\n        }\r\n\r\n        // Status 2 = Video đã bị khóa\r\n        if (status.status === VideoUploadStatus.LOCKED) {\r\n          throw new ZaloSDKError(\r\n            `Video has been locked: ${status.status_message}`,\r\n            -1\r\n          );\r\n        }\r\n\r\n        // Status 5 = Video đã bị xóa\r\n        if (status.status === VideoUploadStatus.DELETED) {\r\n          throw new ZaloSDKError(\r\n            `Video has been deleted: ${status.status_message}`,\r\n            -1\r\n          );\r\n        }\r\n\r\n        // Continue polling for status 3 (đang được xử lý) or 0 (không xác định)\r\n        if (status.status === VideoUploadStatus.PROCESSING || status.status === VideoUploadStatus.UNKNOWN) {\r\n          await this.sleep(pollInterval);\r\n          continue;\r\n        }\r\n\r\n        // Unknown status\r\n        throw new ZaloSDKError(\r\n          `Unknown video processing status: ${status.status}`,\r\n          -1\r\n        );\r\n      } catch (error) {\r\n        if (error instanceof ZaloSDKError) {\r\n          throw error;\r\n        }\r\n        // Continue polling on network errors\r\n        await this.sleep(pollInterval);\r\n      }\r\n    }\r\n\r\n    throw new ZaloSDKError(\r\n      `Video processing timeout after ${maxWaitTime / 1000} seconds`,\r\n      -1\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Upload video from URL\r\n   * @param accessToken OA access token\r\n   * @param videoUrl URL of the video to upload\r\n   * @returns Token for tracking upload progress\r\n   */\r\n  async uploadVideoFromUrl(\r\n    accessToken: string,\r\n    videoUrl: string\r\n  ): Promise<VideoUploadResponse> {\r\n    try {\r\n      if (!videoUrl || videoUrl.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Video URL cannot be empty\", -1);\r\n      }\r\n\r\n      // Validate URL format\r\n      try {\r\n        new URL(videoUrl);\r\n      } catch {\r\n        throw new ZaloSDKError(\"Invalid video URL format\", -1);\r\n      }\r\n\r\n      // Download video from URL\r\n      const response = await fetch(videoUrl);\r\n      if (!response.ok) {\r\n        throw new ZaloSDKError(\r\n          `Failed to download video from URL: ${response.statusText}`,\r\n          -1\r\n        );\r\n      }\r\n\r\n      const videoBuffer = Buffer.from(await response.arrayBuffer());\r\n      const filename = this.extractFilenameFromUrl(videoUrl) || \"video.mp4\";\r\n\r\n      // Upload the downloaded video\r\n      return await this.uploadVideo(accessToken, videoBuffer, filename);\r\n    } catch (error) {\r\n      throw this.handleVideoUploadError(\r\n        error,\r\n        \"Failed to upload video from URL\"\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get video information by video ID\r\n   * @param accessToken OA access token\r\n   * @param videoId Video ID\r\n   * @returns Video information\r\n   */\r\n  async getVideoInfo(accessToken: string, videoId: string): Promise<any> {\r\n    try {\r\n      if (!videoId || videoId.trim() === \"\") {\r\n        throw new ZaloSDKError(\"Video ID cannot be empty\", -1);\r\n      }\r\n\r\n      const response = await this.client.apiGet<any>(\r\n        this.endpoints.video.info,\r\n        accessToken,\r\n        { video_id: videoId }\r\n      );\r\n\r\n      return response;\r\n    } catch (error) {\r\n      throw this.handleVideoUploadError(error, \"Failed to get video info\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Upload video and wait for completion - Combined method\r\n   * @param accessToken OA access token\r\n   * @param file Video file (Buffer or File)\r\n   * @param filename Original filename (optional, will be generated if not provided)\r\n   * @param maxWaitTime Maximum wait time in milliseconds (default: 5 minutes)\r\n   * @param pollInterval Polling interval in milliseconds (default: 5 seconds)\r\n   * @returns Final video status when completed\r\n   */\r\n  async uploadVideoAndWaitForCompletion(\r\n    accessToken: string,\r\n    file: Buffer | File,\r\n    filename?: string,\r\n    maxWaitTime: number = 5 * 60 * 1000, // 5 minutes\r\n    pollInterval: number = 5 * 1000 // 5 seconds\r\n  ): Promise<VideoStatusResponse> {\r\n    try {\r\n      // Step 1: Upload video and get token\r\n      const uploadResult: VideoUploadResponse = await this.uploadVideo(accessToken, file, filename);\r\n\r\n      // Step 2: Wait for upload completion and return final status\r\n      return await this.waitForUploadCompletion(\r\n        accessToken,\r\n        uploadResult.token,\r\n        maxWaitTime,\r\n        pollInterval\r\n      );\r\n    } catch (error) {\r\n      throw this.handleVideoUploadError(\r\n        error,\r\n        \"Failed to upload video and wait for completion\"\r\n      );\r\n    }\r\n  }\r\n\r\n  // ==================== VALIDATION METHODS ====================\r\n\r\n  /**\r\n   * Validate video file\r\n   */\r\n  private validateVideoFile(file: Buffer | File, filename?: string): void {\r\n    if (!file) {\r\n      throw new ZaloSDKError(\"Video file cannot be empty\", -1);\r\n    }\r\n\r\n    let fileSize: number;\r\n    let fileName: string;\r\n    let mimeType: string;\r\n\r\n    if (Buffer.isBuffer(file)) {\r\n      fileSize = file.length;\r\n      fileName = filename || \"video.mp4\";\r\n      mimeType = this.getMimeTypeFromFilename(fileName);\r\n    } else {\r\n      // Handle File input with proper type casting\r\n      const fileObj = file as File;\r\n      fileSize = fileObj.size;\r\n      fileName = fileObj.name || filename || \"video.mp4\";\r\n      mimeType = fileObj.type || this.getMimeTypeFromFilename(fileName);\r\n    }\r\n\r\n    // Check file size (50MB)\r\n    if (fileSize > VIDEO_CONSTRAINTS.maxSize) {\r\n      throw new ZaloSDKError(\r\n        `Video file size exceeds ${\r\n          VIDEO_CONSTRAINTS.maxSize / (1024 * 1024)\r\n        }MB limit`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    // Check file extension\r\n    const fileExtension = fileName\r\n      .toLowerCase()\r\n      .substring(fileName.lastIndexOf(\".\"));\r\n    if (!VIDEO_CONSTRAINTS.allowedExtensions.includes(fileExtension)) {\r\n      throw new ZaloSDKError(\r\n        `Unsupported video format. Only ${VIDEO_CONSTRAINTS.allowedExtensions.join(\r\n          \", \"\r\n        )} are supported`,\r\n        -1\r\n      );\r\n    }\r\n\r\n    // Check MIME type\r\n    if (!VIDEO_CONSTRAINTS.allowedMimeTypes.includes(mimeType)) {\r\n      throw new ZaloSDKError(\r\n        `Unsupported video MIME type. Only ${VIDEO_CONSTRAINTS.allowedMimeTypes.join(\r\n          \", \"\r\n        )} are supported`,\r\n        -1\r\n      );\r\n    }\r\n  }\r\n\r\n  // ==================== HELPER METHODS ====================\r\n\r\n  private getMimeTypeFromFilename(filename: string): string {\r\n    const extension = filename\r\n      .toLowerCase()\r\n      .substring(filename.lastIndexOf(\".\"));\r\n    switch (extension) {\r\n      case \".mp4\":\r\n        return \"video/mp4\";\r\n      case \".avi\":\r\n        return \"video/x-msvideo\";\r\n      default:\r\n        return \"video/mp4\"; // Default fallback\r\n    }\r\n  }\r\n\r\n  private extractFilenameFromUrl(url: string): string | null {\r\n    try {\r\n      const urlObj = new URL(url);\r\n      const pathname = urlObj.pathname;\r\n      const filename = pathname.substring(pathname.lastIndexOf(\"/\") + 1);\r\n      return filename || null;\r\n    } catch {\r\n      return null;\r\n    }\r\n  }\r\n\r\n  private sleep(ms: number): Promise<void> {\r\n    return new Promise((resolve) => setTimeout(resolve, ms));\r\n  }\r\n\r\n\r\n\r\n  private handleVideoUploadError(error: any, defaultMessage: string): Error {\r\n    if (error instanceof ZaloSDKError) {\r\n      return error;\r\n    }\r\n    if (error.response?.data) {\r\n      const errorData = error.response.data;\r\n      return new ZaloSDKError(\r\n        `${defaultMessage}: ${\r\n          errorData.message || errorData.error || \"Unknown error\"\r\n        }`,\r\n        errorData.error || -1,\r\n        errorData\r\n      );\r\n    }\r\n    return new ZaloSDKError(\r\n      `${defaultMessage}: ${error.message || \"Unknown error\"}`,\r\n      -1,\r\n      error\r\n    );\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  MessageRecipient,\r\n  SendMessageResponse,\r\n  TextMessage,\r\n  ConsultationQuoteMessage,\r\n} from \"../types/message\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\nimport {\r\n  TemplateAction,\r\n  TemplateElement,\r\n  TemplateButton,\r\n  MessageItem,\r\n  SendMessageSequenceRequest,\r\n  SendMessageSequenceResponse,\r\n  SendMessageSequenceToMultipleUsersRequest,\r\n  SendMessageSequenceToMultipleUsersResponse,\r\n  UserProgressInfo,\r\n  UserCustomMessage,\r\n  SendCustomMessageSequenceToMultipleUsersRequest,\r\n  SendCustomMessageSequenceToMultipleUsersResponse,\r\n} from \"../types/consultation\";\r\n\r\n\r\n\r\n/**\r\n * Service xử lý các API tin nhắn tư vấn của Zalo Official Account\r\n *\r\n * Tin nhắn tư vấn (CS - Customer Service) là loại tin nhắn đặc biệt\r\n * cho phép OA gửi tin nhắn chủ động đến người dùng trong khung thời gian nhất định\r\n *\r\n * ĐIỀU KIỆN GỬI TIN TƯ VẤN:\r\n *\r\n * 1. THỜI GIAN GỬI:\r\n *    - Chỉ được gửi trong vòng 48 giờ kể từ khi người dùng tương tác cuối cùng với OA\r\n *    - Tương tác bao gồm: gửi tin nhắn, nhấn button, gọi điện, truy cập website từ OA\r\n *\r\n * 2. NỘI DUNG TIN NHẮN:\r\n *    - Phải liên quan đến tư vấn, hỗ trợ khách hàng\r\n *    - Bao gồm: trả lời câu hỏi, hướng dẫn sử dụng, hỗ trợ kỹ thuật\r\n *    - Không được chứa nội dung quảng cáo trực tiếp\r\n *\r\n * 3. TẦN SUẤT GỬI:\r\n *    - Không giới hạn số lượng tin nhắn tư vấn trong ngày\r\n *    - Tuy nhiên cần tuân thủ nguyên tắc không spam\r\n *\r\n * 4. NGƯỜI DÙNG:\r\n *    - Người dùng phải đã follow OA\r\n *    - Người dùng không được block OA\r\n *    - Người dùng phải có tương tác gần đây với OA\r\n */\r\nexport class ConsultationService {\r\n  // Zalo API endpoint for consultation messages\r\n  private readonly endpoint = \"https://openapi.zalo.me/v3.0/oa/message/cs\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn văn bản\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param message Nội dung tin nhắn văn bản\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   */\r\n  public async sendTextMessage(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    message: TextMessage\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validate text length theo quy định của Zalo\r\n      if (!message.text || message.text.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Nội dung tin nhắn không được để trống\", -1);\r\n      }\r\n\r\n      if (message.text.length > 2000) {\r\n        throw new ZaloSDKError(\r\n          \"Nội dung tin nhắn không được vượt quá 2000 ký tự\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Request structure theo API spec\r\n      const request = {\r\n        recipient: {\r\n          user_id: recipient.user_id,\r\n        },\r\n        message: {\r\n          text: message.text,\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation text message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation text message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn trích dẫn (quote message)\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param text Nội dung tin nhắn trả lời (tối đa 2000 ký tự)\r\n   * @param quoteMessageId ID của tin nhắn muốn trích dẫn\r\n   * @returns Thông tin tin nhắn đã gửi với quota information\r\n   */\r\n  public async sendQuoteMessage(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    text: string,\r\n    quoteMessageId: string\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validate input parameters\r\n      if (!text || text.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Nội dung tin nhắn không được để trống\", -1);\r\n      }\r\n\r\n      if (text.length > 2000) {\r\n        throw new ZaloSDKError(\r\n          \"Nội dung tin nhắn không được vượt quá 2000 ký tự\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      if (!quoteMessageId || quoteMessageId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Quote message ID không được để trống\", -1);\r\n      }\r\n\r\n      if (!recipient.user_id || recipient.user_id.trim().length === 0) {\r\n        throw new ZaloSDKError(\"User ID không được để trống\", -1);\r\n      }\r\n\r\n      // Request structure theo API spec\r\n      const request = {\r\n        recipient: {\r\n          user_id: recipient.user_id,\r\n        },\r\n        message: {\r\n          text: text.trim(),\r\n          quote_message_id: quoteMessageId.trim(),\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation quote message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation quote message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Tạo ConsultationQuoteMessage object\r\n   * @param text Nội dung tin nhắn trả lời\r\n   * @param quoteMessageId ID của tin nhắn muốn trích dẫn\r\n   * @param quoteContent Nội dung của tin nhắn được trích dẫn (optional)\r\n   * @returns ConsultationQuoteMessage object\r\n   */\r\n  public createQuoteMessage(\r\n    text: string,\r\n    quoteMessageId: string,\r\n    quoteContent?: string\r\n  ): ConsultationQuoteMessage {\r\n    return {\r\n      type: \"consultation_quote\",\r\n      text: text.trim(),\r\n      quote: {\r\n        message_id: quoteMessageId.trim(),\r\n        content: quoteContent || \"\",\r\n      },\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn hình ảnh bằng URL\r\n   * \r\n   * API Specification:\r\n   * - URL: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * - Method: POST\r\n   * - Content-Type: application/json\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param imageUrl URL trực tiếp đến hình ảnh (jpg, png, tối đa 1MB, tỷ lệ tối ưu 16:9)\r\n   * @param text Tiêu đề ảnh (optional, tối đa 2000 ký tự)\r\n   * @returns Thông tin tin nhắn đã gửi với quota information\r\n   */\r\n  public async sendImageMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    imageUrl: string,\r\n    text?: string\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      if (!imageUrl || imageUrl.trim().length === 0) {\r\n        throw new ZaloSDKError(\"URL hình ảnh không được để trống\", -1);\r\n      }\r\n\r\n      if (text && text.length > 2000) {\r\n        throw new ZaloSDKError(\r\n          \"Tiêu đề ảnh không được vượt quá 2000 ký tự\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Request structure theo API specification v3.0\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          ...(text && text.trim().length > 0 && { text: text.trim() }),\r\n          attachment: {\r\n            type: \"template\",\r\n            payload: {\r\n              template_type: \"media\",\r\n              elements: [\r\n                {\r\n                  media_type: \"image\",\r\n                  url: imageUrl.trim(),\r\n                },\r\n              ],\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation image message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation image message: ${\r\n          (error as Error).message\r\n        }`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn hình ảnh bằng attachment_id (ảnh đã upload trước)\r\n   * \r\n   * API Specification:\r\n   * - URL: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * - Method: POST\r\n   * - Content-Type: application/json\r\n   * - Lưu ý: Chỉ sử dụng attachment_id HOẶC url, không được dùng cả hai\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param attachmentId ID của ảnh đã upload trước đó (từ API upload ảnh)\r\n   * @param text Tiêu đề ảnh (optional, tối đa 2000 ký tự)\r\n   * @returns Thông tin tin nhắn đã gửi với quota information\r\n   */\r\n  public async sendImageByAttachmentId(\r\n    accessToken: string,\r\n    userId: string,\r\n    attachmentId: string,\r\n    text?: string\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      if (!attachmentId || attachmentId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Attachment ID không được để trống\", -1);\r\n      }\r\n\r\n      if (text && text.length > 2000) {\r\n        throw new ZaloSDKError(\r\n          \"Tiêu đề ảnh không được vượt quá 2000 ký tự\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Request structure theo API specification v3.0\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          ...(text && text.trim().length > 0 && { text: text.trim() }),\r\n          attachment: {\r\n            type: \"template\",\r\n            payload: {\r\n              template_type: \"media\",\r\n              elements: [\r\n                {\r\n                  media_type: \"image\",\r\n                  attachment_id: attachmentId.trim(),\r\n                },\r\n              ],\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation image message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation image message: ${\r\n          (error as Error).message\r\n        }`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn hình ảnh (hỗ trợ cả URL và attachment_id)\r\n   * \r\n   * API Specification:\r\n   * - URL: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * - Method: POST\r\n   * - Content-Type: application/json\r\n   * - Lưu ý: Chỉ sử dụng MỘT trong hai: imageUrl HOẶC attachmentId\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param options Tùy chọn gửi ảnh\r\n   * @param options.imageUrl URL trực tiếp đến hình ảnh (jpg, png, tối đa 1MB)\r\n   * @param options.attachmentId ID của ảnh đã upload trước đó\r\n   * @param options.text Tiêu đề ảnh (optional, tối đa 2000 ký tự)\r\n   * @returns Thông tin tin nhắn đã gửi với quota information\r\n   */\r\n  public async sendImage(\r\n    accessToken: string,\r\n    userId: string,\r\n    options: {\r\n      imageUrl?: string;\r\n      attachmentId?: string;\r\n      text?: string;\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validation: phải có ít nhất một trong hai\r\n      if (!options.imageUrl && !options.attachmentId) {\r\n        throw new ZaloSDKError(\r\n          \"Phải cung cấp imageUrl hoặc attachmentId\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Validation: không được có cả hai\r\n      if (options.imageUrl && options.attachmentId) {\r\n        throw new ZaloSDKError(\r\n          \"Chỉ được sử dụng imageUrl HOẶC attachmentId, không được cả hai\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Validation imageUrl\r\n      if (options.imageUrl && options.imageUrl.trim().length === 0) {\r\n        throw new ZaloSDKError(\"URL hình ảnh không được để trống\", -1);\r\n      }\r\n\r\n      // Validation attachmentId\r\n      if (options.attachmentId && options.attachmentId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Attachment ID không được để trống\", -1);\r\n      }\r\n\r\n      // Validation text\r\n      if (options.text && options.text.length > 2000) {\r\n        throw new ZaloSDKError(\r\n          \"Tiêu đề ảnh không được vượt quá 2000 ký tự\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Tạo element dựa trên loại input\r\n      const element: any = {\r\n        media_type: \"image\",\r\n      };\r\n\r\n      if (options.imageUrl) {\r\n        element.url = options.imageUrl.trim();\r\n      } else if (options.attachmentId) {\r\n        element.attachment_id = options.attachmentId.trim();\r\n      }\r\n\r\n      // Request structure theo API specification v3.0\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          ...(options.text && options.text.trim().length > 0 && { \r\n            text: options.text.trim() \r\n          }),\r\n          attachment: {\r\n            type: \"template\",\r\n            payload: {\r\n              template_type: \"media\",\r\n              elements: [element],\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation image message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation image message: ${\r\n          (error as Error).message\r\n        }`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn ảnh GIF động\r\n   * \r\n   * API Specification:\r\n   * - URL: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * - Method: POST\r\n   * - Content-Type: application/json\r\n   * - Lưu ý: width và height là BẮT BUỘC cho media_type = \"gif\"\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param gifUrl URL trực tiếp đến ảnh GIF (tối đa 1MB)\r\n   * @param width Chiều rộng của ảnh GIF (bắt buộc, > 0)\r\n   * @param height Chiều cao của ảnh GIF (bắt buộc, > 0)\r\n   * @param text Tiêu đề ảnh (optional, tối đa 2000 ký tự)\r\n   * @returns Thông tin tin nhắn đã gửi với quota information\r\n   */\r\n  public async sendGifMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    gifUrl: string,\r\n    width: number,\r\n    height: number,\r\n    text?: string\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      if (!gifUrl || gifUrl.trim().length === 0) {\r\n        throw new ZaloSDKError(\"URL GIF không được để trống\", -1);\r\n      }\r\n\r\n      if (width <= 0 || height <= 0) {\r\n        throw new ZaloSDKError(\"Chiều rộng và chiều cao phải lớn hơn 0\", -1);\r\n      }\r\n\r\n      if (text && text.length > 2000) {\r\n        throw new ZaloSDKError(\r\n          \"Tiêu đề ảnh không được vượt quá 2000 ký tự\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Request structure theo API specification v3.0\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          ...(text && text.trim().length > 0 && { text: text.trim() }),\r\n          attachment: {\r\n            type: \"template\",\r\n            payload: {\r\n              template_type: \"media\",\r\n              elements: [\r\n                {\r\n                  media_type: \"gif\",\r\n                  url: gifUrl.trim(),\r\n                  width: width,\r\n                  height: height,\r\n                },\r\n              ],\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation gif message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation gif message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn đính kèm file\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * Method: POST\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param fileToken Token của file đã upload (từ API upload file)\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   *\r\n   * Note: Cần sử dụng API upload file trước để lấy token\r\n   */\r\n  public async sendFileMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    fileToken: string\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      if (!fileToken || fileToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"File token không được để trống\", -1);\r\n      }\r\n\r\n      // Request structure theo API spec\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          attachment: {\r\n            type: \"file\",\r\n            payload: {\r\n              token: fileToken,\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation file message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation file message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn theo mẫu yêu cầu thông tin người dùng\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * Method: POST\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param title Tiêu đề hiển thị của template (tối đa 100 ký tự)\r\n   * @param subtitle Tiêu đề phụ của template (tối đa 500 ký tự)\r\n   * @param imageUrl Đường dẫn đến ảnh\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   */\r\n  public async sendRequestUserInfoMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    title: string,\r\n    subtitle: string,\r\n    imageUrl: string\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validation theo API spec\r\n      if (!title || title.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Tiêu đề không được để trống\", -1);\r\n      }\r\n\r\n      if (title.length > 100) {\r\n        throw new ZaloSDKError(\"Tiêu đề không được vượt quá 100 ký tự\", -1);\r\n      }\r\n\r\n      if (!subtitle || subtitle.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Tiêu đề phụ không được để trống\", -1);\r\n      }\r\n\r\n      if (subtitle.length > 500) {\r\n        throw new ZaloSDKError(\"Tiêu đề phụ không được vượt quá 500 ký tự\", -1);\r\n      }\r\n\r\n      if (!imageUrl || imageUrl.trim().length === 0) {\r\n        throw new ZaloSDKError(\"URL hình ảnh không được để trống\", -1);\r\n      }\r\n\r\n      // Request structure theo API spec\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          attachment: {\r\n            type: \"template\",\r\n            payload: {\r\n              template_type: \"request_user_info\",\r\n              elements: [\r\n                {\r\n                  title: title,\r\n                  subtitle: subtitle,\r\n                  image_url: imageUrl,\r\n                },\r\n              ],\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send request user info message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send request user info message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn kèm Sticker\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * Method: POST\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param stickerAttachmentId ID của sticker (lấy từ https://stickers.zaloapp.com/)\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   *\r\n   * Note: Sticker ID lấy từ nguồn https://stickers.zaloapp.com/\r\n   * Xem video hướng dẫn: https://vimeo.com/649330161\r\n   */\r\n  public async sendStickerMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    stickerAttachmentId: string\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      if (!stickerAttachmentId || stickerAttachmentId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Sticker attachment ID không được để trống\", -1);\r\n      }\r\n\r\n      // Request structure theo API spec\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          attachment: {\r\n            type: \"template\",\r\n            payload: {\r\n              template_type: \"media\",\r\n              elements: [\r\n                {\r\n                  media_type: \"sticker\",\r\n                  attachment_id: stickerAttachmentId,\r\n                },\r\n              ],\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send consultation sticker message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send consultation sticker message: ${\r\n          (error as Error).message\r\n        }`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi chuỗi tin nhắn tư vấn với delay tùy chỉnh\r\n   * Hỗ trợ tất cả các loại tin nhắn: text, image, gif, file, sticker, request_user_info\r\n   *\r\n   * @param request Thông tin request gửi chuỗi tin nhắn\r\n   * @returns Kết quả gửi từng tin nhắn\r\n   */\r\n  public async sendMessageSequence(\r\n    request: SendMessageSequenceRequest\r\n  ): Promise<SendMessageSequenceResponse> {\r\n    const startTime = Date.now();\r\n    const results: SendMessageSequenceResponse[\"results\"] = [];\r\n    let successCount = 0;\r\n    let failureCount = 0;\r\n\r\n    try {\r\n      // Validate input\r\n      if (!request.accessToken || request.accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.userId || request.userId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"User ID không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.messages || request.messages.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách tin nhắn không được để trống\", -1);\r\n      }\r\n\r\n      // Gửi từng tin nhắn theo thứ tự\r\n      for (let i = 0; i < request.messages.length; i++) {\r\n        const message = request.messages[i];\r\n        const messageResult = {\r\n          index: i,\r\n          type: message.type,\r\n          success: false,\r\n          timestamp: Date.now(),\r\n        } as SendMessageSequenceResponse[\"results\"][0];\r\n\r\n        try {\r\n          let response: SendMessageResponse;\r\n\r\n          // Gửi tin nhắn theo loại\r\n          switch (message.type) {\r\n            case \"text\":\r\n              if (!message.text) {\r\n                throw new Error(\"Text không được để trống cho text message\");\r\n              }\r\n              response = await this.sendTextMessage(\r\n                request.accessToken,\r\n                { user_id: request.userId },\r\n                { type: \"text\", text: message.text }\r\n              );\r\n              break;\r\n\r\n            case \"image\":\r\n              if (message.imageUrl) {\r\n                response = await this.sendImageMessage(\r\n                  request.accessToken,\r\n                  request.userId,\r\n                  message.imageUrl,\r\n                  message.text\r\n                );\r\n              } else if (message.attachmentId) {\r\n                response = await this.sendImageByAttachmentId(\r\n                  request.accessToken,\r\n                  request.userId,\r\n                  message.attachmentId,\r\n                  message.text\r\n                );\r\n              } else {\r\n                throw new Error(\"imageUrl hoặc attachmentId là bắt buộc cho image message\");\r\n              }\r\n              break;\r\n\r\n            case \"gif\":\r\n              if (!message.gifUrl || !message.width || !message.height) {\r\n                throw new Error(\"gifUrl, width và height là bắt buộc cho gif message\");\r\n              }\r\n              response = await this.sendGifMessage(\r\n                request.accessToken,\r\n                request.userId,\r\n                message.gifUrl,\r\n                message.width,\r\n                message.height,\r\n                message.text\r\n              );\r\n              break;\r\n\r\n            case \"file\":\r\n              if (!message.fileToken) {\r\n                throw new Error(\"fileToken là bắt buộc cho file message\");\r\n              }\r\n              response = await this.sendFileMessage(\r\n                request.accessToken,\r\n                request.userId,\r\n                message.fileToken\r\n              );\r\n              break;\r\n\r\n            case \"sticker\":\r\n              if (!message.stickerAttachmentId) {\r\n                throw new Error(\"stickerAttachmentId là bắt buộc cho sticker message\");\r\n              }\r\n              response = await this.sendStickerMessage(\r\n                request.accessToken,\r\n                request.userId,\r\n                message.stickerAttachmentId\r\n              );\r\n              break;\r\n\r\n            case \"request_user_info\":\r\n              if (!message.title || !message.subtitle || !message.imageUrl) {\r\n                throw new Error(\"title, subtitle và imageUrl là bắt buộc cho request_user_info message\");\r\n              }\r\n              response = await this.sendRequestUserInfoMessage(\r\n                request.accessToken,\r\n                request.userId,\r\n                message.title,\r\n                message.subtitle,\r\n                message.imageUrl\r\n              );\r\n              break;\r\n\r\n            default:\r\n              throw new Error(`Loại tin nhắn không được hỗ trợ: ${message.type}`);\r\n          }\r\n\r\n          // Ghi nhận thành công\r\n          messageResult.success = true;\r\n          messageResult.response = response;\r\n          successCount++;\r\n\r\n        } catch (error) {\r\n          // Ghi nhận thất bại\r\n          messageResult.success = false;\r\n          messageResult.error = error instanceof Error ? error.message : String(error);\r\n          failureCount++;\r\n        }\r\n\r\n        results.push(messageResult);\r\n\r\n        // Delay trước khi gửi tin nhắn tiếp theo (trừ tin nhắn cuối cùng)\r\n        if (i < request.messages.length - 1) {\r\n          const delayTime = message.delay ?? request.defaultDelay ?? 0;\r\n          if (delayTime > 0) {\r\n            await this.sleep(delayTime);\r\n          }\r\n        }\r\n      }\r\n\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      return {\r\n        successCount,\r\n        failureCount,\r\n        results,\r\n        totalDuration,\r\n      };\r\n\r\n    } catch (error) {\r\n      // Lỗi tổng quát\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n\r\n      throw new ZaloSDKError(\r\n        `Failed to send message sequence: ${(error as Error).message}`,\r\n        -1,\r\n        {\r\n          successCount,\r\n          failureCount,\r\n          results,\r\n          totalDuration,\r\n        }\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi chuỗi tin nhắn tư vấn tới nhiều users với callback tracking\r\n   *\r\n   * @param request Thông tin request gửi chuỗi tin nhắn tới nhiều users\r\n   * @returns Kết quả gửi tin nhắn cho tất cả users\r\n   */\r\n  public async sendMessageSequenceToMultipleUsers(\r\n    request: SendMessageSequenceToMultipleUsersRequest\r\n  ): Promise<SendMessageSequenceToMultipleUsersResponse> {\r\n    const startTime = Date.now();\r\n    const userResults: SendMessageSequenceToMultipleUsersResponse[\"userResults\"] = [];\r\n    let successfulUsers = 0;\r\n    let failedUsers = 0;\r\n    let totalSuccessfulMessages = 0;\r\n    let totalFailedMessages = 0;\r\n\r\n    try {\r\n      // Validate input\r\n      if (!request.accessToken || request.accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.userIds || request.userIds.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách user IDs không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.messages || request.messages.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách tin nhắn không được để trống\", -1);\r\n      }\r\n\r\n      // Loại bỏ user IDs trùng lặp và rỗng\r\n      const uniqueUserIds = [...new Set(request.userIds.filter(id => id && id.trim().length > 0))];\r\n\r\n      if (uniqueUserIds.length === 0) {\r\n        throw new ZaloSDKError(\"Không có user ID hợp lệ nào\", -1);\r\n      }\r\n\r\n      // Gửi tin nhắn cho từng user tuần tự\r\n      for (let i = 0; i < uniqueUserIds.length; i++) {\r\n        const userId = uniqueUserIds[i];\r\n        const userStartTime = Date.now();\r\n\r\n        // Callback: Bắt đầu gửi cho user\r\n        if (request.onProgress) {\r\n          request.onProgress({\r\n            userId,\r\n            userIndex: i,\r\n            totalUsers: uniqueUserIds.length,\r\n            status: 'started',\r\n            startTime: userStartTime,\r\n          });\r\n        }\r\n\r\n        const userResult = {\r\n          userId,\r\n          userIndex: i,\r\n          success: false,\r\n          startTime: userStartTime,\r\n          endTime: 0,\r\n          duration: 0,\r\n        } as SendMessageSequenceToMultipleUsersResponse[\"userResults\"][0];\r\n\r\n        try {\r\n          // Gửi chuỗi tin nhắn cho user này\r\n          const messageSequenceResult = await this.sendMessageSequence({\r\n            accessToken: request.accessToken,\r\n            userId,\r\n            messages: request.messages,\r\n            defaultDelay: request.defaultDelay,\r\n          });\r\n\r\n          // Ghi nhận thành công\r\n          const userEndTime = Date.now();\r\n          userResult.success = true;\r\n          userResult.messageSequenceResult = messageSequenceResult;\r\n          userResult.endTime = userEndTime;\r\n          userResult.duration = userEndTime - userStartTime;\r\n\r\n          successfulUsers++;\r\n          totalSuccessfulMessages += messageSequenceResult.successCount;\r\n          totalFailedMessages += messageSequenceResult.failureCount;\r\n\r\n          // Callback: Hoàn thành thành công\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              userId,\r\n              userIndex: i,\r\n              totalUsers: uniqueUserIds.length,\r\n              status: 'completed',\r\n              result: messageSequenceResult,\r\n              startTime: userStartTime,\r\n              endTime: userEndTime,\r\n            });\r\n          }\r\n\r\n        } catch (error) {\r\n          // Ghi nhận thất bại\r\n          const userEndTime = Date.now();\r\n          const errorMessage = error instanceof Error ? error.message : String(error);\r\n\r\n          userResult.success = false;\r\n          userResult.error = errorMessage;\r\n          userResult.endTime = userEndTime;\r\n          userResult.duration = userEndTime - userStartTime;\r\n\r\n          failedUsers++;\r\n          // Với user thất bại, coi như tất cả tin nhắn đều thất bại\r\n          totalFailedMessages += request.messages.length;\r\n\r\n          // Callback: Thất bại\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              userId,\r\n              userIndex: i,\r\n              totalUsers: uniqueUserIds.length,\r\n              status: 'failed',\r\n              error: errorMessage,\r\n              startTime: userStartTime,\r\n              endTime: userEndTime,\r\n            });\r\n          }\r\n        }\r\n\r\n        userResults.push(userResult);\r\n\r\n        // Delay giữa các user (trừ user cuối cùng)\r\n        if (i < uniqueUserIds.length - 1 && request.delayBetweenUsers && request.delayBetweenUsers > 0) {\r\n          await this.sleep(request.delayBetweenUsers);\r\n        }\r\n      }\r\n\r\n      const totalDuration = Date.now() - startTime;\r\n      const totalMessages = uniqueUserIds.length * request.messages.length;\r\n\r\n      return {\r\n        totalUsers: uniqueUserIds.length,\r\n        successfulUsers,\r\n        failedUsers,\r\n        userResults,\r\n        totalDuration,\r\n        messageStats: {\r\n          totalSuccessfulMessages,\r\n          totalFailedMessages,\r\n          totalMessages,\r\n        },\r\n      };\r\n\r\n    } catch (error) {\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n\r\n      throw new ZaloSDKError(\r\n        `Failed to send message sequence to multiple users: ${(error as Error).message}`,\r\n        -1,\r\n        {\r\n          totalUsers: request.userIds?.length || 0,\r\n          successfulUsers,\r\n          failedUsers,\r\n          userResults,\r\n          totalDuration,\r\n          messageStats: {\r\n            totalSuccessfulMessages,\r\n            totalFailedMessages,\r\n            totalMessages: (request.userIds?.length || 0) * (request.messages?.length || 0),\r\n          },\r\n        }\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn tư vấn tùy chỉnh tới nhiều users với callback tracking\r\n   * Mỗi user có thể có bộ tin nhắn riêng biệt\r\n   *\r\n   * @param request Thông tin request gửi tin nhắn tùy chỉnh tới nhiều users\r\n   * @returns Kết quả gửi tin nhắn cho tất cả users\r\n   */\r\n  public async sendCustomMessageSequenceToMultipleUsers(\r\n    request: SendCustomMessageSequenceToMultipleUsersRequest\r\n  ): Promise<SendCustomMessageSequenceToMultipleUsersResponse> {\r\n    const startTime = Date.now();\r\n    const userResults: SendCustomMessageSequenceToMultipleUsersResponse[\"userResults\"] = [];\r\n    let successfulUsers = 0;\r\n    let failedUsers = 0;\r\n    let totalSuccessfulMessages = 0;\r\n    let totalFailedMessages = 0;\r\n\r\n    try {\r\n      // Validate input\r\n      if (!request.accessToken || request.accessToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Access token không được để trống\", -1);\r\n      }\r\n\r\n      if (!request.userCustomMessages || request.userCustomMessages.length === 0) {\r\n        throw new ZaloSDKError(\"Danh sách user custom messages không được để trống\", -1);\r\n      }\r\n\r\n      // Validate từng user custom message\r\n      const validUserCustomMessages: UserCustomMessage[] = [];\r\n      const seenUserIds = new Set<string>();\r\n\r\n      for (let i = 0; i < request.userCustomMessages.length; i++) {\r\n        const userCustomMessage = request.userCustomMessages[i];\r\n\r\n        // Validate userId\r\n        if (!userCustomMessage.userId || userCustomMessage.userId.trim().length === 0) {\r\n          throw new ZaloSDKError(`User custom message ${i + 1}: userId không được để trống`, -1);\r\n        }\r\n\r\n        // Check duplicate userId\r\n        if (seenUserIds.has(userCustomMessage.userId)) {\r\n          throw new ZaloSDKError(`User ID \"${userCustomMessage.userId}\" bị trùng lặp`, -1);\r\n        }\r\n        seenUserIds.add(userCustomMessage.userId);\r\n\r\n        // Validate messages\r\n        if (!userCustomMessage.messages || userCustomMessage.messages.length === 0) {\r\n          throw new ZaloSDKError(`User custom message ${i + 1}: danh sách tin nhắn không được để trống`, -1);\r\n        }\r\n\r\n        validUserCustomMessages.push(userCustomMessage);\r\n      }\r\n\r\n      // Gửi tin nhắn cho từng user tuần tự\r\n      for (let i = 0; i < validUserCustomMessages.length; i++) {\r\n        const userCustomMessage = validUserCustomMessages[i];\r\n        const userId = userCustomMessage.userId;\r\n        const userStartTime = Date.now();\r\n\r\n        // Callback: Bắt đầu gửi cho user\r\n        if (request.onProgress) {\r\n          request.onProgress({\r\n            userId,\r\n            userIndex: i,\r\n            totalUsers: validUserCustomMessages.length,\r\n            status: 'started',\r\n            startTime: userStartTime,\r\n          });\r\n        }\r\n\r\n        const userResult = {\r\n          userId,\r\n          userIndex: i,\r\n          success: false,\r\n          startTime: userStartTime,\r\n          endTime: 0,\r\n          duration: 0,\r\n        } as SendCustomMessageSequenceToMultipleUsersResponse[\"userResults\"][0];\r\n\r\n        try {\r\n          // Gửi chuỗi tin nhắn tùy chỉnh cho user này\r\n          const messageSequenceResult = await this.sendMessageSequence({\r\n            accessToken: request.accessToken,\r\n            userId,\r\n            messages: userCustomMessage.messages,\r\n            defaultDelay: request.defaultDelay,\r\n          });\r\n\r\n          // Ghi nhận thành công\r\n          const userEndTime = Date.now();\r\n          userResult.success = true;\r\n          userResult.messageSequenceResult = messageSequenceResult;\r\n          userResult.endTime = userEndTime;\r\n          userResult.duration = userEndTime - userStartTime;\r\n\r\n          successfulUsers++;\r\n          totalSuccessfulMessages += messageSequenceResult.successCount;\r\n          totalFailedMessages += messageSequenceResult.failureCount;\r\n\r\n          // Callback: Hoàn thành thành công\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              userId,\r\n              userIndex: i,\r\n              totalUsers: validUserCustomMessages.length,\r\n              status: 'completed',\r\n              result: messageSequenceResult,\r\n              startTime: userStartTime,\r\n              endTime: userEndTime,\r\n            });\r\n          }\r\n\r\n        } catch (error) {\r\n          // Ghi nhận thất bại\r\n          const userEndTime = Date.now();\r\n          const errorMessage = error instanceof Error ? error.message : String(error);\r\n\r\n          userResult.success = false;\r\n          userResult.error = errorMessage;\r\n          userResult.endTime = userEndTime;\r\n          userResult.duration = userEndTime - userStartTime;\r\n\r\n          failedUsers++;\r\n          // Với user thất bại, coi như tất cả tin nhắn đều thất bại\r\n          totalFailedMessages += userCustomMessage.messages.length;\r\n\r\n          // Callback: Thất bại\r\n          if (request.onProgress) {\r\n            request.onProgress({\r\n              userId,\r\n              userIndex: i,\r\n              totalUsers: validUserCustomMessages.length,\r\n              status: 'failed',\r\n              error: errorMessage,\r\n              startTime: userStartTime,\r\n              endTime: userEndTime,\r\n            });\r\n          }\r\n        }\r\n\r\n        userResults.push(userResult);\r\n\r\n        // Delay giữa các user (trừ user cuối cùng)\r\n        if (i < validUserCustomMessages.length - 1 && request.delayBetweenUsers && request.delayBetweenUsers > 0) {\r\n          await this.sleep(request.delayBetweenUsers);\r\n        }\r\n      }\r\n\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      // Tính tổng số tin nhắn\r\n      const totalMessages = validUserCustomMessages.reduce((total, userCustomMessage) => {\r\n        return total + userCustomMessage.messages.length;\r\n      }, 0);\r\n\r\n      return {\r\n        totalUsers: validUserCustomMessages.length,\r\n        successfulUsers,\r\n        failedUsers,\r\n        userResults,\r\n        totalDuration,\r\n        messageStats: {\r\n          totalSuccessfulMessages,\r\n          totalFailedMessages,\r\n          totalMessages,\r\n        },\r\n      };\r\n\r\n    } catch (error) {\r\n      const totalDuration = Date.now() - startTime;\r\n\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n\r\n      // Tính tổng số tin nhắn cho error response\r\n      const totalMessages = request.userCustomMessages?.reduce((total, userCustomMessage) => {\r\n        return total + (userCustomMessage.messages?.length || 0);\r\n      }, 0) || 0;\r\n\r\n      throw new ZaloSDKError(\r\n        `Failed to send custom message sequence to multiple users: ${(error as Error).message}`,\r\n        -1,\r\n        {\r\n          totalUsers: request.userCustomMessages?.length || 0,\r\n          successfulUsers,\r\n          failedUsers,\r\n          userResults,\r\n          totalDuration,\r\n          messageStats: {\r\n            totalSuccessfulMessages,\r\n            totalFailedMessages,\r\n            totalMessages,\r\n          },\r\n        }\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn template tổng quát với elements và buttons\r\n   * Endpoint: https://openapi.zalo.me/v3.0/oa/message/cs\r\n   * Method: POST\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID người nhận (user_id)\r\n   * @param templateType Loại template (ví dụ: \"request_user_info\")\r\n   * @param elements Mảng elements (tối đa 5 phần tử)\r\n   * @param buttons Mảng buttons (tối đa 5 phần tử, optional)\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   */\r\n  public async sendTemplateMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    templateType: string,\r\n    elements: TemplateElement[],\r\n    buttons?: TemplateButton[]\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validation cơ bản\r\n      if (!templateType || templateType.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Template type không được để trống\", -1);\r\n      }\r\n\r\n      if (!elements || elements.length === 0) {\r\n        throw new ZaloSDKError(\"Elements không được để trống\", -1);\r\n      }\r\n\r\n      if (elements.length > 5) {\r\n        throw new ZaloSDKError(\"Elements không được vượt quá 5 phần tử\", -1);\r\n      }\r\n\r\n      if (buttons && buttons.length > 5) {\r\n        throw new ZaloSDKError(\"Buttons không được vượt quá 5 phần tử\", -1);\r\n      }\r\n\r\n      // Validate từng element\r\n      elements.forEach((element, index) => {\r\n        this.validateTemplateElement(element, index);\r\n      });\r\n\r\n      // Validate từng button (nếu có)\r\n      if (buttons) {\r\n        buttons.forEach((button, index) => {\r\n          this.validateTemplateButton(button, index);\r\n        });\r\n      }\r\n\r\n      // Request structure theo API specification\r\n      const request = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          attachment: {\r\n            type: \"template\",\r\n            payload: {\r\n              template_type: templateType,\r\n              elements: elements,\r\n              ...(buttons && buttons.length > 0 && { buttons: buttons }),\r\n            },\r\n          },\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send template message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send template message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate template element theo quy tắc API\r\n   * @param element Template element cần validate\r\n   * @param index Index của element trong mảng (để báo lỗi)\r\n   */\r\n  private validateTemplateElement(element: TemplateElement, index: number): void {\r\n    // title là bắt buộc, ≤ 100 ký tự\r\n    if (!element.title || element.title.trim().length === 0) {\r\n      throw new ZaloSDKError(`Element ${index + 1}: title không được để trống`, -1);\r\n    }\r\n\r\n    if (element.title.length > 100) {\r\n      throw new ZaloSDKError(`Element ${index + 1}: title không được vượt quá 100 ký tự`, -1);\r\n    }\r\n\r\n    // subtitle: bắt buộc cho element đầu tiên, tùy chọn cho các element sau, ≤ 500 ký tự\r\n    if (index === 0) {\r\n      if (!element.subtitle || element.subtitle.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Element đầu tiên phải có subtitle\", -1);\r\n      }\r\n    }\r\n\r\n    if (element.subtitle && element.subtitle.length > 500) {\r\n      throw new ZaloSDKError(`Element ${index + 1}: subtitle không được vượt quá 500 ký tự`, -1);\r\n    }\r\n\r\n    // Validate default_action nếu có\r\n    if (element.default_action) {\r\n      this.validateTemplateAction(element.default_action, `Element ${index + 1} default_action`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate template button theo quy tắc API\r\n   * @param button Template button cần validate\r\n   * @param index Index của button trong mảng (để báo lỗi)\r\n   */\r\n  private validateTemplateButton(button: TemplateButton, index: number): void {\r\n    // title là bắt buộc, ≤ 100 ký tự\r\n    if (!button.title || button.title.trim().length === 0) {\r\n      throw new ZaloSDKError(`Button ${index + 1}: title không được để trống`, -1);\r\n    }\r\n\r\n    if (button.title.length > 100) {\r\n      throw new ZaloSDKError(`Button ${index + 1}: title không được vượt quá 100 ký tự`, -1);\r\n    }\r\n\r\n    // type là bắt buộc\r\n    if (!button.type || button.type.trim().length === 0) {\r\n      throw new ZaloSDKError(`Button ${index + 1}: type không được để trống`, -1);\r\n    }\r\n\r\n    // Validate payload theo type\r\n    this.validateButtonPayload(button, index);\r\n  }\r\n\r\n  /**\r\n   * Validate template action (cho default_action)\r\n   * @param action Template action cần validate\r\n   * @param context Context để báo lỗi\r\n   */\r\n  private validateTemplateAction(action: TemplateAction, context: string): void {\r\n    if (!action.type || action.type.trim().length === 0) {\r\n      throw new ZaloSDKError(`${context}: type không được để trống`, -1);\r\n    }\r\n\r\n    const validActionTypes = [\"oa.open.url\", \"oa.query.show\", \"oa.query.hide\", \"oa.open.sms\", \"oa.open.phone\"];\r\n    if (!validActionTypes.includes(action.type)) {\r\n      throw new ZaloSDKError(`${context}: type phải là một trong ${validActionTypes.join(\", \")}`, -1);\r\n    }\r\n\r\n    // Validate payload theo type\r\n    switch (action.type) {\r\n      case \"oa.open.url\":\r\n        if (!action.url || action.url.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: url không được để trống cho type oa.open.url`, -1);\r\n        }\r\n        break;\r\n\r\n      case \"oa.query.show\":\r\n      case \"oa.query.hide\":\r\n        if (typeof action.payload !== 'string') {\r\n          throw new ZaloSDKError(`${context}: payload phải là string cho type ${action.type}`, -1);\r\n        }\r\n        if (!action.payload || action.payload.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: payload không được để trống cho type ${action.type}`, -1);\r\n        }\r\n        if (action.payload.length > 1000) {\r\n          throw new ZaloSDKError(`${context}: payload không được vượt quá 1000 ký tự`, -1);\r\n        }\r\n        break;\r\n\r\n      case \"oa.open.sms\":\r\n        if (!action.payload || typeof action.payload !== 'object') {\r\n          throw new ZaloSDKError(`${context}: payload phải là object cho type oa.open.sms`, -1);\r\n        }\r\n        const smsPayload = action.payload as { content?: string; phone_code?: string };\r\n        if (!smsPayload.phone_code || smsPayload.phone_code.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: payload.phone_code không được để trống`, -1);\r\n        }\r\n        if (smsPayload.content && smsPayload.content.length > 160) {\r\n          throw new ZaloSDKError(`${context}: payload.content không được vượt quá 160 ký tự`, -1);\r\n        }\r\n        break;\r\n\r\n      case \"oa.open.phone\":\r\n        if (!action.payload || typeof action.payload !== 'object') {\r\n          throw new ZaloSDKError(`${context}: payload phải là object cho type oa.open.phone`, -1);\r\n        }\r\n        const phonePayload = action.payload as { phone_code?: string };\r\n        if (!phonePayload.phone_code || phonePayload.phone_code.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: payload.phone_code không được để trống`, -1);\r\n        }\r\n        break;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate button payload theo type\r\n   * @param button Template button cần validate\r\n   * @param index Index của button trong mảng (để báo lỗi)\r\n   */\r\n  private validateButtonPayload(button: TemplateButton, index: number): void {\r\n    const context = `Button ${index + 1}`;\r\n\r\n    switch (button.type) {\r\n      case \"oa.open.url\":\r\n        if (!button.payload || typeof button.payload !== 'object') {\r\n          throw new ZaloSDKError(`${context}: payload phải là object cho type oa.open.url`, -1);\r\n        }\r\n        const urlPayload = button.payload as { url?: string };\r\n        if (!urlPayload.url || urlPayload.url.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: payload.url không được để trống`, -1);\r\n        }\r\n        break;\r\n\r\n      case \"oa.query.show\":\r\n      case \"oa.query.hide\":\r\n        if (typeof button.payload !== 'string') {\r\n          throw new ZaloSDKError(`${context}: payload phải là string cho type ${button.type}`, -1);\r\n        }\r\n        if (!button.payload || button.payload.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: payload không được để trống`, -1);\r\n        }\r\n        if (button.payload.length > 1000) {\r\n          throw new ZaloSDKError(`${context}: payload không được vượt quá 1000 ký tự`, -1);\r\n        }\r\n        break;\r\n\r\n      case \"oa.open.sms\":\r\n        if (!button.payload || typeof button.payload !== 'object') {\r\n          throw new ZaloSDKError(`${context}: payload phải là object cho type oa.open.sms`, -1);\r\n        }\r\n        const smsPayload = button.payload as { content?: string; phone_code?: string };\r\n        if (!smsPayload.phone_code || smsPayload.phone_code.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: payload.phone_code không được để trống`, -1);\r\n        }\r\n        if (smsPayload.content && smsPayload.content.length > 160) {\r\n          throw new ZaloSDKError(`${context}: payload.content không được vượt quá 160 ký tự`, -1);\r\n        }\r\n        break;\r\n\r\n      case \"oa.open.phone\":\r\n        if (!button.payload || typeof button.payload !== 'object') {\r\n          throw new ZaloSDKError(`${context}: payload phải là object cho type oa.open.phone`, -1);\r\n        }\r\n        const phonePayload = button.payload as { phone_code?: string };\r\n        if (!phonePayload.phone_code || phonePayload.phone_code.trim().length === 0) {\r\n          throw new ZaloSDKError(`${context}: payload.phone_code không được để trống`, -1);\r\n        }\r\n        break;\r\n\r\n      default:\r\n        const validButtonTypes = [\"oa.open.url\", \"oa.query.show\", \"oa.query.hide\", \"oa.open.sms\", \"oa.open.phone\"];\r\n        throw new ZaloSDKError(`${context}: type phải là một trong ${validButtonTypes.join(\", \")}`, -1);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template element\r\n   * @param title Tiêu đề (bắt buộc, ≤ 100 ký tự)\r\n   * @param subtitle Tiêu đề phụ (≤ 500 ký tự)\r\n   * @param imageUrl URL ảnh (optional)\r\n   * @param defaultAction Hành động khi click (optional)\r\n   * @returns TemplateElement object\r\n   */\r\n  public createTemplateElement(\r\n    title: string,\r\n    subtitle?: string,\r\n    imageUrl?: string,\r\n    defaultAction?: TemplateAction\r\n  ): TemplateElement {\r\n    return {\r\n      title,\r\n      ...(subtitle && { subtitle }),\r\n      ...(imageUrl && { image_url: imageUrl }),\r\n      ...(defaultAction && { default_action: defaultAction }),\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template button với URL\r\n   * @param title Tiêu đề button (≤ 100 ký tự)\r\n   * @param url URL để mở\r\n   * @returns TemplateButton object\r\n   */\r\n  public createUrlButton(title: string, url: string): TemplateButton {\r\n    return {\r\n      title,\r\n      type: \"oa.open.url\",\r\n      payload: { url },\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template button với query (hiển thị)\r\n   * @param title Tiêu đề button (≤ 100 ký tự)\r\n   * @param payload Dữ liệu callback (≤ 1000 ký tự)\r\n   * @returns TemplateButton object\r\n   */\r\n  public createQueryShowButton(title: string, payload: string): TemplateButton {\r\n    return {\r\n      title,\r\n      type: \"oa.query.show\",\r\n      payload,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template button với query (ẩn)\r\n   * @param title Tiêu đề button (≤ 100 ký tự)\r\n   * @param payload Dữ liệu callback (≤ 1000 ký tự)\r\n   * @returns TemplateButton object\r\n   */\r\n  public createQueryHideButton(title: string, payload: string): TemplateButton {\r\n    return {\r\n      title,\r\n      type: \"oa.query.hide\",\r\n      payload,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template button với SMS\r\n   * @param title Tiêu đề button (≤ 100 ký tự)\r\n   * @param phoneCode Số điện thoại\r\n   * @param content Nội dung SMS (≤ 160 ký tự)\r\n   * @returns TemplateButton object\r\n   */\r\n  public createSmsButton(title: string, phoneCode: string, content?: string): TemplateButton {\r\n    return {\r\n      title,\r\n      type: \"oa.open.sms\",\r\n      payload: {\r\n        phone_code: phoneCode,\r\n        ...(content && { content }),\r\n      },\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template button với Phone\r\n   * @param title Tiêu đề button (≤ 100 ký tự)\r\n   * @param phoneCode Số điện thoại\r\n   * @returns TemplateButton object\r\n   */\r\n  public createPhoneButton(title: string, phoneCode: string): TemplateButton {\r\n    return {\r\n      title,\r\n      type: \"oa.open.phone\",\r\n      payload: { phone_code: phoneCode },\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template action với URL\r\n   * @param url URL để mở\r\n   * @returns TemplateAction object\r\n   */\r\n  public createUrlAction(url: string): TemplateAction {\r\n    return {\r\n      type: \"oa.open.url\",\r\n      url,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template action với query (hiển thị)\r\n   * @param payload Dữ liệu callback (≤ 1000 ký tự)\r\n   * @returns TemplateAction object\r\n   */\r\n  public createQueryShowAction(payload: string): TemplateAction {\r\n    return {\r\n      type: \"oa.query.show\",\r\n      payload,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template action với query (ẩn)\r\n   * @param payload Dữ liệu callback (≤ 1000 ký tự)\r\n   * @returns TemplateAction object\r\n   */\r\n  public createQueryHideAction(payload: string): TemplateAction {\r\n    return {\r\n      type: \"oa.query.hide\",\r\n      payload,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template action với SMS\r\n   * @param phoneCode Số điện thoại\r\n   * @param content Nội dung SMS (≤ 160 ký tự)\r\n   * @returns TemplateAction object\r\n   */\r\n  public createSmsAction(phoneCode: string, content?: string): TemplateAction {\r\n    return {\r\n      type: \"oa.open.sms\",\r\n      payload: {\r\n        phone_code: phoneCode,\r\n        ...(content && { content }),\r\n      },\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Helper function để tạo template action với Phone\r\n   * @param phoneCode Số điện thoại\r\n   * @returns TemplateAction object\r\n   */\r\n  public createPhoneAction(phoneCode: string): TemplateAction {\r\n    return {\r\n      type: \"oa.open.phone\",\r\n      payload: { phone_code: phoneCode },\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Utility method để sleep/delay\r\n   * @param ms Thời gian delay tính bằng milliseconds\r\n   */\r\n  private sleep(ms: number): Promise<void> {\r\n    return new Promise(resolve => setTimeout(resolve, ms));\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  SendMessageResponse,\r\n  TransactionMessage,\r\n  MessageRecipient,\r\n} from \"../types/message\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\n\r\n/**\r\n * Service xử lý các API tin nhắn giao dịch của Zalo Official Account\r\n *\r\n * ⚠️ **DEPRECATED**: Transaction Message API (v2.0) đã bị deprecated bởi Zalo\r\n *\r\n * **Khuyến nghị**: Sử dụng **ZNS Service** thay thế:\r\n * - `ZNSService.sendMessageByUid()` - Gửi tin nhắn template theo UID\r\n * - `ZNSService.sendMessage()` - Gửi tin nhắn theo số điện thoại\r\n *\r\n * Tham khảo: https://developers.zalo.me/docs/zalo-notification-service/\r\n *\r\n * ===\r\n *\r\n * ĐIỀU KIỆN GỬI TIN GIAO DỊCH:\r\n *\r\n * 1. THỜI GIAN GỬI:\r\n *    - Chỉ được gửi trong vòng 24 giờ kể từ khi người dùng tương tác cuối cùng với OA\r\n *    - Tương tác bao gồm: gửi tin nhắn, nhấn button, gọi điện, truy cập website từ OA\r\n *\r\n * 2. NỘI DUNG TIN NHẮN:\r\n *    - Phải liên quan trực tiếp đến giao dịch thực tế\r\n *    - Bao gồm: xác nhận đơn hàng, thông báo thanh toán, cập nhật trạng thái giao hàng\r\n *    - Không được chứa nội dung quảng cáo, khuyến mãi\r\n *\r\n * 3. TẦN SUẤT GỬI:\r\n *    - Tối đa 3 tin nhắn giao dịch/ngày cho mỗi người dùng\r\n *    - Phải có khoảng cách ít nhất 1 giờ giữa các tin nhắn\r\n *\r\n * 4. ĐỊNH DẠNG:\r\n *    - Phải sử dụng template được Zalo phê duyệt trước\r\n *    - Template phải tuân thủ format chuẩn của tin giao dịch\r\n *\r\n * 5. NGƯỜI DÙNG:\r\n *    - Người dùng phải đã follow OA\r\n *    - Người dùng không được block OA\r\n *    - Người dùng phải có tương tác gần đây với OA\r\n *\r\n * 6. OFFICIAL ACCOUNT:\r\n *    - OA phải được xác minh (verified)\r\n *    - OA phải có quyền gửi tin giao dịch được Zalo cấp phép\r\n *    - OA không được vi phạm chính sách của Zalo\r\n *\r\n * LỖI THƯỜNG GẶP:\r\n * - 1004: Người dùng chưa follow OA hoặc đã unfollow\r\n * - 1005: Vượt quá thời gian 24 giờ từ lần tương tác cuối\r\n * - 1006: Vượt quá giới hạn 3 tin/ngày\r\n * - 1007: Template chưa được phê duyệt hoặc không hợp lệ\r\n * - 1008: Nội dung tin nhắn vi phạm chính sách\r\n *\r\n * @deprecated Sử dụng ZNSService thay vì TransactionService\r\n */\r\nexport class TransactionService {\r\n  private readonly transactionApiUrl =\r\n    \"https://openapi.zalo.me/v2.0/oa/message/transaction\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Gửi tin nhắn giao dịch\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param message Nội dung tin nhắn giao dịch\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendTransactionMessage(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    message: TransactionMessage\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validate transaction message\r\n      this.validateTransactionMessage(message);\r\n\r\n      const request = {\r\n        recipient,\r\n        message,\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.transactionApiUrl, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send transaction message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send transaction message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn xác nhận đơn hàng\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param orderInfo Thông tin đơn hàng\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendOrderConfirmation(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    orderInfo: {\r\n      orderId: string;\r\n      orderDate: string;\r\n      totalAmount: number;\r\n      items: Array<{\r\n        name: string;\r\n        quantity: number;\r\n        price: number;\r\n      }>;\r\n      customerInfo: {\r\n        name: string;\r\n        phone: string;\r\n        address: string;\r\n      };\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      const message: TransactionMessage = {\r\n        type: \"transaction\",\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"transaction\",\r\n            elements: [\r\n              {\r\n                title: `Xác nhận đơn hàng #${orderInfo.orderId}`,\r\n                subtitle: `Ngày đặt: ${orderInfo.orderDate}`,\r\n                default_action: {\r\n                  type: \"web_url\",\r\n                  url: `https://example.com/orders/${orderInfo.orderId}`,\r\n                },\r\n                buttons: [\r\n                  {\r\n                    type: \"web_url\",\r\n                    title: \"Xem chi tiết\",\r\n                    url: `https://example.com/orders/${orderInfo.orderId}`,\r\n                  },\r\n                ],\r\n              },\r\n            ],\r\n          },\r\n        },\r\n      };\r\n\r\n      return this.sendTransactionMessage(accessToken, recipient, message);\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send order confirmation: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn thông báo thanh toán\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param paymentInfo Thông tin thanh toán\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendPaymentNotification(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    paymentInfo: {\r\n      orderId: string;\r\n      amount: number;\r\n      paymentMethod: string;\r\n      paymentDate: string;\r\n      status: \"success\" | \"failed\" | \"pending\";\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      const statusText = {\r\n        success: \"Thành công\",\r\n        failed: \"Thất bại\",\r\n        pending: \"Đang xử lý\",\r\n      };\r\n\r\n      const message: TransactionMessage = {\r\n        type: \"transaction\",\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"transaction\",\r\n            elements: [\r\n              {\r\n                title: `Thông báo thanh toán - ${\r\n                  statusText[paymentInfo.status]\r\n                }`,\r\n                subtitle: `Đơn hàng #${\r\n                  paymentInfo.orderId\r\n                } - ${paymentInfo.amount.toLocaleString(\"vi-VN\")} VNĐ`,\r\n                default_action: {\r\n                  type: \"web_url\",\r\n                  url: `https://example.com/payments/${paymentInfo.orderId}`,\r\n                },\r\n                buttons: [\r\n                  {\r\n                    type: \"web_url\",\r\n                    title: \"Xem chi tiết\",\r\n                    url: `https://example.com/payments/${paymentInfo.orderId}`,\r\n                  },\r\n                ],\r\n              },\r\n            ],\r\n          },\r\n        },\r\n      };\r\n\r\n      return this.sendTransactionMessage(accessToken, recipient, message);\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send payment notification: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn cập nhật trạng thái giao hàng\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param shippingInfo Thông tin giao hàng\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendShippingUpdate(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    shippingInfo: {\r\n      orderId: string;\r\n      trackingNumber: string;\r\n      status: \"preparing\" | \"shipped\" | \"in_transit\" | \"delivered\";\r\n      estimatedDelivery?: string;\r\n      carrier?: string;\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      const statusText = {\r\n        preparing: \"Đang chuẩn bị hàng\",\r\n        shipped: \"Đã giao cho đơn vị vận chuyển\",\r\n        in_transit: \"Đang vận chuyển\",\r\n        delivered: \"Đã giao hàng thành công\",\r\n      };\r\n\r\n      const message: TransactionMessage = {\r\n        type: \"transaction\",\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"transaction\",\r\n            elements: [\r\n              {\r\n                title: `Cập nhật giao hàng - ${\r\n                  statusText[shippingInfo.status]\r\n                }`,\r\n                subtitle: `Đơn hàng #${shippingInfo.orderId} - Mã vận đơn: ${shippingInfo.trackingNumber}`,\r\n                default_action: {\r\n                  type: \"web_url\",\r\n                  url: `https://example.com/tracking/${shippingInfo.trackingNumber}`,\r\n                },\r\n                buttons: [\r\n                  {\r\n                    type: \"web_url\",\r\n                    title: \"Theo dõi đơn hàng\",\r\n                    url: `https://example.com/tracking/${shippingInfo.trackingNumber}`,\r\n                  },\r\n                ],\r\n              },\r\n            ],\r\n          },\r\n        },\r\n      };\r\n\r\n      return this.sendTransactionMessage(accessToken, recipient, message);\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send shipping update: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate transaction message format\r\n   * @param message Transaction message to validate\r\n   */\r\n  private validateTransactionMessage(message: TransactionMessage): void {\r\n    if (!message.attachment) {\r\n      throw new ZaloSDKError(\"Transaction message must have attachment\", -1);\r\n    }\r\n\r\n    if (!message.attachment.payload) {\r\n      throw new ZaloSDKError(\r\n        \"Transaction message attachment must have payload\",\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (message.attachment.payload.template_type !== \"transaction\") {\r\n      throw new ZaloSDKError(\r\n        \"Transaction message must use transaction template type\",\r\n        -1\r\n      );\r\n    }\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  SendMessageResponse,\r\n  MessageRecipient,\r\n} from \"../types/message\";\r\nimport {\r\n  PromotionMessage,\r\n  PromotionUserResult,\r\n  MultiplePromotionProgress,\r\n  MultiplePromotionResult,\r\n  PromotionButtonInput,\r\n  BannerConfig,\r\n} from \"../types/promotion\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\n\r\n/**\r\n * Service xử lý các API tin nhắn truyền thông/quảng cáo của Zalo Official Account\r\n *\r\n * ⚠️ **DEPRECATED**: Promotion Message API (v3.0) đã bị deprecated bởi Zalo\r\n *\r\n * **Khuyến nghị**: Sử dụng **ZNS Service** thay thế:\r\n * - `ZNSService.sendMessageByUid()` - Gửi tin nhắn template theo UID\r\n * - `ZNSService.sendMessage()` - Gửi tin nhắn theo số điện thoại\r\n *\r\n * Tham khảo: https://developers.zalo.me/docs/zalo-notification-service/\r\n *\r\n * ===\r\n *\r\n * ĐIỀU KIỆN GỬI TIN TRUYỀN THÔNG:\r\n *\r\n * 1. THỜI GIAN GỬI:\r\n *    - Chỉ được gửi trong khung giờ từ 8:00 - 22:00 hàng ngày\r\n *    - Không được gửi vào các ngày lễ, tết theo quy định\r\n *\r\n * 2. NỘI DUNG TIN NHẮN:\r\n *    - Phải là nội dung quảng cáo, khuyến mãi, tin tức\r\n *    - Không được chứa nội dung vi phạm pháp luật\r\n *    - Phải tuân thủ quy định về quảng cáo của Việt Nam\r\n *\r\n * 3. TẦN SUẤT GỬI:\r\n *    - Tối đa 1 tin nhắn truyền thông/ngày cho mỗi người dùng\r\n *    - Người dùng có thể từ chối nhận tin truyền thông\r\n *\r\n * 4. ĐỊNH DẠNG:\r\n *    - Phải sử dụng template được Zalo phê duyệt trước\r\n *    - Template phải tuân thủ format chuẩn của tin truyền thông\r\n *\r\n * 5. NGƯỜI DÙNG:\r\n *    - Người dùng phải đã follow OA\r\n *    - Người dùng không được block OA\r\n *    - Người dùng không được từ chối nhận tin truyền thông\r\n *\r\n * 6. OFFICIAL ACCOUNT:\r\n *    - OA phải được xác minh (verified)\r\n *    - OA phải có quyền gửi tin truyền thông được Zalo cấp phép\r\n *    - OA không được vi phạm chính sách về quảng cáo\r\n *\r\n * LỖI THƯỜNG GẶP:\r\n * - 2001: Ngoài khung giờ cho phép (8:00-22:00)\r\n * - 2002: Người dùng đã từ chối nhận tin truyền thông\r\n * - 2003: Vượt quá giới hạn 1 tin/ngày\r\n * - 2004: Template chưa được phê duyệt\r\n * - 2005: Nội dung vi phạm chính sách quảng cáo\r\n * - 2006: OA chưa có quyền gửi tin truyền thông\r\n *\r\n * @deprecated Sử dụng ZNSService thay vì PromotionService\r\n */\r\nexport class PromotionService {\r\n  private readonly promotionApiUrl =\r\n    \"https://openapi.zalo.me/v3.0/oa/message/promotion\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Convert input button to proper typed button for API\r\n   * @param button Input button with flexible typing\r\n   * @returns Properly typed button for API\r\n   */\r\n  private convertToTypedButton(button: PromotionButtonInput): any {\r\n    const baseButton = {\r\n      title: button.title,\r\n      image_icon: button.imageIcon || \"\",\r\n      type: button.type,\r\n    };\r\n\r\n    // Convert payload based on button type using discriminated union\r\n    switch (button.type) {\r\n      case \"oa.open.url\":\r\n        return {\r\n          ...baseButton,\r\n          payload: { url: button.payload.url }\r\n        };\r\n\r\n      case \"oa.query.show\":\r\n      case \"oa.query.hide\":\r\n        return {\r\n          ...baseButton,\r\n          payload: button.payload // Already string type\r\n        };\r\n\r\n      case \"oa.open.sms\":\r\n        return {\r\n          ...baseButton,\r\n          payload: {\r\n            content: button.payload.content,\r\n            phone_code: button.payload.phone_code || button.payload.phoneCode || \"\"\r\n          }\r\n        };\r\n\r\n      case \"oa.open.phone\":\r\n        return {\r\n          ...baseButton,\r\n          payload: typeof button.payload === 'string'\r\n            ? { phone_code: button.payload }\r\n            : { phone_code: button.payload.phone_code || button.payload.phoneCode || \"\" }\r\n        };\r\n\r\n      default:\r\n        // This should never happen with proper typing\r\n        return {\r\n          ...baseButton,\r\n          payload: {}\r\n        };\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn truyền thông/quảng cáo\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param message Nội dung tin nhắn truyền thông\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendPromotionMessage(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    message: PromotionMessage\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validate promotion message\r\n      this.validatePromotionMessage(message);\r\n\r\n      // Check sending time (8:00 - 22:00)\r\n      this.validateSendingTime();\r\n\r\n      const request = {\r\n        recipient,\r\n        message,\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(this.promotionApiUrl, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send promotion message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send promotion message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn khuyến mãi sản phẩm\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param promotionInfo Thông tin khuyến mãi\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendProductPromotion(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    promotionInfo: {\r\n      title: string;\r\n      description: string;\r\n      imageUrl?: string;\r\n      attachmentId?: string;\r\n      originalPrice: number;\r\n      discountPrice: number;\r\n      discountPercent: number;\r\n      validUntil: string;\r\n      productUrl: string;\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      const message: PromotionMessage = {\r\n        type: \"promotion\",\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"promotion\",\r\n            language: \"VI\",\r\n            elements: [\r\n              // Banner element\r\n              {\r\n                type: \"banner\",\r\n                ...(promotionInfo.attachmentId\r\n                  ? { attachment_id: promotionInfo.attachmentId }\r\n                  : { image_url: promotionInfo.imageUrl }\r\n                ),\r\n              },\r\n              // Header element\r\n              {\r\n                type: \"header\",\r\n                content: `💥💥 ${promotionInfo.title} 💥💥`,\r\n                align: \"center\",\r\n              },\r\n              // Description text\r\n              {\r\n                type: \"text\",\r\n                content: promotionInfo.description,\r\n                align: \"left\",\r\n              },\r\n              // Price table\r\n              {\r\n                type: \"table\",\r\n                content: [\r\n                  {\r\n                    key: \"Giá gốc\",\r\n                    value: `${promotionInfo.originalPrice.toLocaleString('vi-VN')}đ`,\r\n                  },\r\n                  {\r\n                    key: \"Giá khuyến mãi\",\r\n                    value: `${promotionInfo.discountPrice.toLocaleString('vi-VN')}đ`,\r\n                  },\r\n                  {\r\n                    key: \"Giảm giá\",\r\n                    value: `${promotionInfo.discountPercent}%`,\r\n                  },\r\n                  {\r\n                    key: \"Hạn sử dụng\",\r\n                    value: promotionInfo.validUntil,\r\n                  },\r\n                ],\r\n              },\r\n              // Footer text\r\n              {\r\n                type: \"text\",\r\n                content: \"🛒 Nhanh tay đặt hàng để không bỏ lỡ ưu đãi!\",\r\n                align: \"center\",\r\n              },\r\n            ],\r\n            buttons: [\r\n              {\r\n                title: \"Mua ngay\",\r\n                image_icon: \"\",\r\n                type: \"oa.open.url\",\r\n                payload: {\r\n                  url: promotionInfo.productUrl,\r\n                },\r\n              },\r\n              {\r\n                title: \"Xem thêm sản phẩm\",\r\n                image_icon: \"\",\r\n                type: \"oa.query.hide\",\r\n                payload: \"#xemthem\",\r\n              },\r\n            ] as any,\r\n          },\r\n        },\r\n      };\r\n\r\n      return this.sendPromotionMessage(accessToken, recipient, message);\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send product promotion: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn thông báo sự kiện\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param eventInfo Thông tin sự kiện\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendEventNotification(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    eventInfo: {\r\n      title: string;\r\n      description: string;\r\n      imageUrl?: string;\r\n      attachmentId?: string;\r\n      eventDate: string;\r\n      location: string;\r\n      registrationUrl: string;\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      const message: PromotionMessage = {\r\n        type: \"promotion\",\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"promotion\",\r\n            language: \"VI\",\r\n            elements: [\r\n              // Banner element\r\n              {\r\n                type: \"banner\",\r\n                ...(eventInfo.attachmentId\r\n                  ? { attachment_id: eventInfo.attachmentId }\r\n                  : { image_url: eventInfo.imageUrl }\r\n                ),\r\n              },\r\n              // Header element\r\n              {\r\n                type: \"header\",\r\n                content: `🎉 ${eventInfo.title} 🎉`,\r\n                align: \"center\",\r\n              },\r\n              // Description text\r\n              {\r\n                type: \"text\",\r\n                content: eventInfo.description,\r\n                align: \"left\",\r\n              },\r\n              // Event details table\r\n              {\r\n                type: \"table\",\r\n                content: [\r\n                  {\r\n                    key: \"Thời gian\",\r\n                    value: eventInfo.eventDate,\r\n                  },\r\n                  {\r\n                    key: \"Địa điểm\",\r\n                    value: eventInfo.location,\r\n                  },\r\n                ],\r\n              },\r\n              // Call to action text\r\n              {\r\n                type: \"text\",\r\n                content: \"🎫 Đăng ký ngay để không bỏ lỡ sự kiện!\",\r\n                align: \"center\",\r\n              },\r\n            ],\r\n            buttons: [\r\n              {\r\n                title: \"Đăng ký tham gia\",\r\n                image_icon: \"\",\r\n                type: \"oa.open.url\",\r\n                payload: {\r\n                  url: eventInfo.registrationUrl,\r\n                },\r\n              },\r\n              {\r\n                title: \"Chia sẻ sự kiện\",\r\n                image_icon: \"\",\r\n                type: \"oa.query.hide\",\r\n                payload: \"#chiase\",\r\n              },\r\n            ] as any,\r\n          },\r\n        },\r\n      };\r\n\r\n      return this.sendPromotionMessage(accessToken, recipient, message);\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send event notification: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn newsletter\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param newsletterInfo Thông tin newsletter\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendNewsletter(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    newsletterInfo: {\r\n      title: string;\r\n      summary: string;\r\n      imageUrl?: string;\r\n      attachmentId?: string;\r\n      articles: Array<{\r\n        title: string;\r\n        url: string;\r\n      }>;\r\n      unsubscribeUrl: string;\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      const message: PromotionMessage = {\r\n        type: \"promotion\",\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"promotion\",\r\n            language: \"VI\",\r\n            elements: [\r\n              // Banner element\r\n              {\r\n                type: \"banner\",\r\n                ...(newsletterInfo.attachmentId\r\n                  ? { attachment_id: newsletterInfo.attachmentId }\r\n                  : { image_url: newsletterInfo.imageUrl }\r\n                ),\r\n              },\r\n              // Header element\r\n              {\r\n                type: \"header\",\r\n                content: `📰 ${newsletterInfo.title}`,\r\n                align: \"center\",\r\n              },\r\n              // Summary text\r\n              {\r\n                type: \"text\",\r\n                content: newsletterInfo.summary,\r\n                align: \"left\",\r\n              },\r\n              // Articles table\r\n              {\r\n                type: \"table\",\r\n                content: newsletterInfo.articles.slice(0, 5).map((article, index) => ({\r\n                  key: `Bài viết ${index + 1}`,\r\n                  value: article.title,\r\n                })),\r\n              },\r\n              // Footer text\r\n              {\r\n                type: \"text\",\r\n                content: \"📖 Đọc ngay để cập nhật thông tin mới nhất!\",\r\n                align: \"center\",\r\n              },\r\n            ],\r\n            buttons: [\r\n              {\r\n                title: \"Đọc toàn bộ\",\r\n                image_icon: \"\",\r\n                type: \"oa.open.url\",\r\n                payload: {\r\n                  url: newsletterInfo.articles[0]?.url || \"#\",\r\n                },\r\n              },\r\n              {\r\n                title: \"Hủy đăng ký\",\r\n                image_icon: \"\",\r\n                type: \"oa.open.url\",\r\n                payload: {\r\n                  url: newsletterInfo.unsubscribeUrl,\r\n                },\r\n              },\r\n            ] as any,\r\n          },\r\n        },\r\n      };\r\n\r\n      return this.sendPromotionMessage(accessToken, recipient, message);\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send newsletter: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Tạo promotion message theo format chuẩn v3.0 (như trong docs)\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param promotionData Dữ liệu promotion\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendCustomPromotion(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    promotionData: {\r\n      banner: BannerConfig;\r\n      headerContent: string;\r\n      textContent: string;\r\n      tableData: Array<{ key: string; value: string }>;\r\n      footerText?: string;\r\n      buttons: PromotionButtonInput[];\r\n      language?: \"VI\" | \"EN\";\r\n    }\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      // Validate banner configuration\r\n      this.validateBannerConfig(promotionData.banner);\r\n\r\n      const elements: any[] = [\r\n        // Banner element\r\n        {\r\n          type: \"banner\",\r\n          ...promotionData.banner,\r\n        },\r\n        // Header element\r\n        {\r\n          type: \"header\",\r\n          content: promotionData.headerContent,\r\n        },\r\n        // Text element\r\n        {\r\n          type: \"text\",\r\n          align: \"left\",\r\n          content: promotionData.textContent,\r\n        },\r\n      ];\r\n\r\n      // Add table if provided\r\n      if (promotionData.tableData && promotionData.tableData.length > 0) {\r\n        elements.push({\r\n          type: \"table\",\r\n          content: promotionData.tableData,\r\n        });\r\n      }\r\n\r\n      // Add footer text if provided\r\n      if (promotionData.footerText) {\r\n        elements.push({\r\n          type: \"text\",\r\n          align: \"center\",\r\n          content: promotionData.footerText,\r\n        });\r\n      }\r\n\r\n      const message: PromotionMessage = {\r\n        type: \"promotion\",\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"promotion\",\r\n            language: promotionData.language || \"VI\",\r\n            elements,\r\n            buttons: promotionData.buttons.map(btn => this.convertToTypedButton(btn)),\r\n          },\r\n        },\r\n      };\r\n\r\n      return this.sendPromotionMessage(accessToken, recipient, message);\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send custom promotion: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate banner configuration\r\n   * @param banner Banner configuration to validate\r\n   */\r\n  private validateBannerConfig(banner: BannerConfig): void {\r\n    if (!banner.image_url && !banner.attachment_id) {\r\n      throw new ZaloSDKError(\r\n        \"Banner must have either image_url or attachment_id\",\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (banner.image_url && banner.attachment_id) {\r\n      throw new ZaloSDKError(\r\n        \"Banner cannot have both image_url and attachment_id. Use only one.\",\r\n        -1\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate promotion message format\r\n   * @param message Promotion message to validate\r\n   */\r\n  private validatePromotionMessage(message: PromotionMessage): void {\r\n    if (!message.attachment) {\r\n      throw new ZaloSDKError(\"Promotion message must have attachment\", -1);\r\n    }\r\n\r\n    if (!message.attachment.payload) {\r\n      throw new ZaloSDKError(\r\n        \"Promotion message attachment must have payload\",\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (message.attachment.payload.template_type !== \"promotion\") {\r\n      throw new ZaloSDKError(\r\n        \"Promotion message must use promotion template type\",\r\n        -1\r\n      );\r\n    }\r\n\r\n    if (!message.attachment.payload.elements || message.attachment.payload.elements.length === 0) {\r\n      throw new ZaloSDKError(\r\n        \"Promotion message must have at least one element\",\r\n        -1\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi promotion message đến nhiều user_ids với callback tracking\r\n   * @param accessToken Access token của Official Account\r\n   * @param userIds Danh sách user IDs cần gửi\r\n   * @param promotionData Dữ liệu promotion\r\n   * @param options Tùy chọn gửi và tracking\r\n   * @returns Kết quả gửi đến tất cả users\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendCustomPromotionToMultipleUsers(\r\n    accessToken: string,\r\n    userIds: string[],\r\n    promotionData: {\r\n      banner: BannerConfig;\r\n      headerContent: string;\r\n      textContent: string;\r\n      tableData: Array<{ key: string; value: string }>;\r\n      footerText?: string;\r\n      buttons: PromotionButtonInput[];\r\n      language?: \"VI\" | \"EN\";\r\n    },\r\n    options?: {\r\n      mode?: \"sequential\" | \"parallel\"; // Chế độ gửi: tuần tự hoặc song song\r\n      delayMs?: number; // Delay giữa các tin nhắn (chỉ cho sequential mode)\r\n      onProgress?: (progress: MultiplePromotionProgress) => void; // Callback tracking\r\n      onUserComplete?: (result: PromotionUserResult) => void; // Callback cho từng user\r\n      continueOnError?: boolean; // Tiếp tục khi có lỗi (default: true)\r\n    }\r\n  ): Promise<MultiplePromotionResult> {\r\n    try {\r\n      // Validate inputs\r\n      if (!userIds || userIds.length === 0) {\r\n        throw new ZaloSDKError(\"User IDs array cannot be empty\", -1);\r\n      }\r\n\r\n      // Remove duplicates\r\n      const uniqueUserIds = [...new Set(userIds)];\r\n\r\n      // Validate sending time\r\n      this.validateSendingTime();\r\n\r\n      const startTime = Date.now();\r\n      const results: PromotionUserResult[] = [];\r\n      const mode = options?.mode || \"sequential\";\r\n      const delayMs = options?.delayMs || 1000;\r\n      const continueOnError = options?.continueOnError !== false;\r\n\r\n      // Initial progress callback\r\n      if (options?.onProgress) {\r\n        options.onProgress({\r\n          total: uniqueUserIds.length,\r\n          completed: 0,\r\n          successful: 0,\r\n          failed: 0,\r\n          currentUserId: null,\r\n          startTime,\r\n          estimatedTimeRemaining: null,\r\n        });\r\n      }\r\n\r\n      if (mode === \"parallel\") {\r\n        // Gửi song song\r\n        const promises = uniqueUserIds.map(async (userId) => {\r\n          try {\r\n            const result = await this.sendCustomPromotion(\r\n              accessToken,\r\n              { user_id: userId },\r\n              promotionData\r\n            );\r\n\r\n            const userResult: PromotionUserResult = {\r\n              userId,\r\n              success: true,\r\n              result,\r\n              error: null,\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            return userResult;\r\n          } catch (error) {\r\n            const userResult: PromotionUserResult = {\r\n              userId,\r\n              success: false,\r\n              result: null,\r\n              error: error instanceof ZaloSDKError ? error : new ZaloSDKError(\r\n                `Failed to send to user ${userId}: ${(error as Error).message}`,\r\n                -1,\r\n                error\r\n              ),\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            if (!continueOnError) {\r\n              throw userResult.error;\r\n            }\r\n\r\n            return userResult;\r\n          }\r\n        });\r\n\r\n        const parallelResults = await Promise.all(promises);\r\n        results.push(...parallelResults);\r\n      } else {\r\n        // Gửi tuần tự\r\n        for (let i = 0; i < uniqueUserIds.length; i++) {\r\n          const userId = uniqueUserIds[i];\r\n\r\n          try {\r\n            // Progress callback\r\n            if (options?.onProgress) {\r\n              const completed = i;\r\n              const successful = results.filter(r => r.success).length;\r\n              const failed = results.filter(r => !r.success).length;\r\n              const elapsed = Date.now() - startTime;\r\n              const avgTimePerUser = elapsed / Math.max(completed, 1);\r\n              const remaining = uniqueUserIds.length - completed;\r\n              const estimatedTimeRemaining = remaining * avgTimePerUser;\r\n\r\n              options.onProgress({\r\n                total: uniqueUserIds.length,\r\n                completed,\r\n                successful,\r\n                failed,\r\n                currentUserId: userId,\r\n                startTime,\r\n                estimatedTimeRemaining,\r\n              });\r\n            }\r\n\r\n            const result = await this.sendCustomPromotion(\r\n              accessToken,\r\n              { user_id: userId },\r\n              promotionData\r\n            );\r\n\r\n            const userResult: PromotionUserResult = {\r\n              userId,\r\n              success: true,\r\n              result,\r\n              error: null,\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            results.push(userResult);\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            // Delay giữa các tin nhắn (trừ tin cuối cùng)\r\n            if (i < uniqueUserIds.length - 1 && delayMs > 0) {\r\n              await new Promise(resolve => setTimeout(resolve, delayMs));\r\n            }\r\n\r\n          } catch (error) {\r\n            const userResult: PromotionUserResult = {\r\n              userId,\r\n              success: false,\r\n              result: null,\r\n              error: error instanceof ZaloSDKError ? error : new ZaloSDKError(\r\n                `Failed to send to user ${userId}: ${(error as Error).message}`,\r\n                -1,\r\n                error\r\n              ),\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            results.push(userResult);\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            if (!continueOnError) {\r\n              throw userResult.error;\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      const endTime = Date.now();\r\n      const successful = results.filter(r => r.success);\r\n      const failed = results.filter(r => !r.success);\r\n\r\n      // Final progress callback\r\n      if (options?.onProgress) {\r\n        options.onProgress({\r\n          total: uniqueUserIds.length,\r\n          completed: uniqueUserIds.length,\r\n          successful: successful.length,\r\n          failed: failed.length,\r\n          currentUserId: null,\r\n          startTime,\r\n          estimatedTimeRemaining: 0,\r\n        });\r\n      }\r\n\r\n      return {\r\n        total: uniqueUserIds.length,\r\n        successful: successful.length,\r\n        failed: failed.length,\r\n        results,\r\n        executionTime: endTime - startTime,\r\n        mode,\r\n        startTime: new Date(startTime),\r\n        endTime: new Date(endTime),\r\n        successRate: (successful.length / uniqueUserIds.length) * 100,\r\n      };\r\n\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send promotion to multiple users: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi promotion message đến nhiều user_ids với promotionData riêng cho từng user\r\n   * @param accessToken Access token của Official Account\r\n   * @param userPromotions Danh sách user và promotionData tương ứng\r\n   * @param options Tùy chọn gửi và tracking\r\n   * @returns Kết quả gửi đến tất cả users\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendPersonalizedPromotionToMultipleUsers(\r\n    accessToken: string,\r\n    userPromotions: Array<{\r\n      userId: string;\r\n      promotionData: {\r\n        banner: BannerConfig;\r\n        headerContent: string;\r\n        textContent: string;\r\n        tableData: Array<{ key: string; value: string }>;\r\n        footerText?: string;\r\n        buttons: PromotionButtonInput[];\r\n        language?: \"VI\" | \"EN\";\r\n      };\r\n    }>,\r\n    options?: {\r\n      mode?: \"sequential\" | \"parallel\"; // Chế độ gửi: tuần tự hoặc song song\r\n      delayMs?: number; // Delay giữa các tin nhắn (chỉ cho sequential mode)\r\n      onProgress?: (progress: MultiplePromotionProgress) => void; // Callback tracking\r\n      onUserComplete?: (result: PromotionUserResult) => void; // Callback cho từng user\r\n      continueOnError?: boolean; // Tiếp tục khi có lỗi (default: true)\r\n    }\r\n  ): Promise<MultiplePromotionResult> {\r\n    try {\r\n      // Validate inputs\r\n      if (!userPromotions || userPromotions.length === 0) {\r\n        throw new ZaloSDKError(\"User promotions array cannot be empty\", -1);\r\n      }\r\n\r\n      // Extract unique user IDs\r\n      const userIds = userPromotions.map(up => up.userId);\r\n      const uniqueUserIds = [...new Set(userIds)];\r\n\r\n      if (uniqueUserIds.length !== userPromotions.length) {\r\n        throw new ZaloSDKError(\"Duplicate user IDs found in user promotions\", -1);\r\n      }\r\n\r\n      // Validate sending time\r\n      this.validateSendingTime();\r\n\r\n      const startTime = Date.now();\r\n      const results: PromotionUserResult[] = [];\r\n      const mode = options?.mode || \"sequential\";\r\n      const delayMs = options?.delayMs || 1000;\r\n      const continueOnError = options?.continueOnError !== false;\r\n\r\n      // Initial progress callback\r\n      if (options?.onProgress) {\r\n        options.onProgress({\r\n          total: uniqueUserIds.length,\r\n          completed: 0,\r\n          successful: 0,\r\n          failed: 0,\r\n          currentUserId: null,\r\n          startTime,\r\n          estimatedTimeRemaining: null,\r\n        });\r\n      }\r\n\r\n      if (mode === \"parallel\") {\r\n        // Gửi song song\r\n        const promises = userPromotions.map(async (userPromotion) => {\r\n          try {\r\n            const result = await this.sendCustomPromotion(\r\n              accessToken,\r\n              { user_id: userPromotion.userId },\r\n              userPromotion.promotionData\r\n            );\r\n\r\n            const userResult: PromotionUserResult = {\r\n              userId: userPromotion.userId,\r\n              success: true,\r\n              result,\r\n              error: null,\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            return userResult;\r\n          } catch (error) {\r\n            const userResult: PromotionUserResult = {\r\n              userId: userPromotion.userId,\r\n              success: false,\r\n              result: null,\r\n              error: error instanceof ZaloSDKError ? error : new ZaloSDKError(\r\n                `Failed to send to user ${userPromotion.userId}: ${(error as Error).message}`,\r\n                -1,\r\n                error\r\n              ),\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            if (!continueOnError) {\r\n              throw userResult.error;\r\n            }\r\n\r\n            return userResult;\r\n          }\r\n        });\r\n\r\n        const parallelResults = await Promise.all(promises);\r\n        results.push(...parallelResults);\r\n      } else {\r\n        // Gửi tuần tự\r\n        for (let i = 0; i < userPromotions.length; i++) {\r\n          const userPromotion = userPromotions[i];\r\n\r\n          try {\r\n            // Progress callback\r\n            if (options?.onProgress) {\r\n              const completed = i;\r\n              const successful = results.filter(r => r.success).length;\r\n              const failed = results.filter(r => !r.success).length;\r\n              const elapsed = Date.now() - startTime;\r\n              const avgTimePerUser = elapsed / Math.max(completed, 1);\r\n              const remaining = userPromotions.length - completed;\r\n              const estimatedTimeRemaining = remaining * avgTimePerUser;\r\n\r\n              options.onProgress({\r\n                total: userPromotions.length,\r\n                completed,\r\n                successful,\r\n                failed,\r\n                currentUserId: userPromotion.userId,\r\n                startTime,\r\n                estimatedTimeRemaining,\r\n              });\r\n            }\r\n\r\n            const result = await this.sendCustomPromotion(\r\n              accessToken,\r\n              { user_id: userPromotion.userId },\r\n              userPromotion.promotionData\r\n            );\r\n\r\n            const userResult: PromotionUserResult = {\r\n              userId: userPromotion.userId,\r\n              success: true,\r\n              result,\r\n              error: null,\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            results.push(userResult);\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            // Delay giữa các tin nhắn (trừ tin cuối cùng)\r\n            if (i < userPromotions.length - 1 && delayMs > 0) {\r\n              await new Promise(resolve => setTimeout(resolve, delayMs));\r\n            }\r\n\r\n          } catch (error) {\r\n            const userResult: PromotionUserResult = {\r\n              userId: userPromotion.userId,\r\n              success: false,\r\n              result: null,\r\n              error: error instanceof ZaloSDKError ? error : new ZaloSDKError(\r\n                `Failed to send to user ${userPromotion.userId}: ${(error as Error).message}`,\r\n                -1,\r\n                error\r\n              ),\r\n              timestamp: new Date(),\r\n            };\r\n\r\n            results.push(userResult);\r\n\r\n            // Callback cho từng user\r\n            if (options?.onUserComplete) {\r\n              options.onUserComplete(userResult);\r\n            }\r\n\r\n            if (!continueOnError) {\r\n              throw userResult.error;\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      const endTime = Date.now();\r\n      const successful = results.filter(r => r.success);\r\n      const failed = results.filter(r => !r.success);\r\n\r\n      // Final progress callback\r\n      if (options?.onProgress) {\r\n        options.onProgress({\r\n          total: userPromotions.length,\r\n          completed: userPromotions.length,\r\n          successful: successful.length,\r\n          failed: failed.length,\r\n          currentUserId: null,\r\n          startTime,\r\n          estimatedTimeRemaining: 0,\r\n        });\r\n      }\r\n\r\n      return {\r\n        total: userPromotions.length,\r\n        successful: successful.length,\r\n        failed: failed.length,\r\n        results,\r\n        executionTime: endTime - startTime,\r\n        mode,\r\n        startTime: new Date(startTime),\r\n        endTime: new Date(endTime),\r\n        successRate: (successful.length / userPromotions.length) * 100,\r\n      };\r\n\r\n    } catch (error) {\r\n      throw new ZaloSDKError(\r\n        `Failed to send personalized promotion to multiple users: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Validate sending time (8:00 - 22:00)\r\n   */\r\n  private validateSendingTime(): void {\r\n    const now = new Date();\r\n    const hour = now.getHours();\r\n\r\n    if (hour < 8 || hour >= 22) {\r\n      throw new ZaloSDKError(\r\n        \"Tin nhắn truyền thông chỉ được gửi trong khung giờ từ 8:00 - 22:00\",\r\n        2001\r\n      );\r\n    }\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport {\r\n  Message,\r\n  SendMessageRequest,\r\n  SendMessageResponse,\r\n  ImageMessage,\r\n  MessageRecipient,\r\n  AnonymousTextMessage,\r\n} from \"../types/message\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\n\r\n/**\r\n * Service xử lý các API tin nhắn tổng quát của Zalo Official Account\r\n *\r\n * Bao gồm các loại tin nhắn:\r\n * - Tin nhắn phản hồi (Response Message)\r\n * - Tin nhắn ẩn danh (Anonymous Message)\r\n * - Tin nhắn tự động (Auto Message)\r\n * - Tin nhắn hệ thống (System Message)\r\n *\r\n * ĐIỀU KIỆN GỬI TIN NHẮN TỔNG QUÁT:\r\n *\r\n * 1. TIN NHẮN PHẢN HỒI:\r\n *    - Chỉ được gửi trong vòng 24 giờ kể từ khi người dùng gửi tin nhắn đến OA\r\n *    - Không giới hạn số lượng tin nhắn phản hồi\r\n *    - Có thể chứa bất kỳ nội dung nào (tư vấn, quảng cáo, thông tin)\r\n *\r\n * 2. TIN NHẮN ẨN DANH:\r\n *    - Gửi tin nhắn mà không hiển thị thông tin OA\r\n *    - Chỉ dành cho các trường hợp đặc biệt\r\n *    - Cần được Zalo phê duyệt trước khi sử dụng\r\n *\r\n * 3. TIN NHẮN TỰ ĐỘNG:\r\n *    - Tin nhắn được gửi tự động dựa trên trigger\r\n *    - Bao gồm: tin chào mừng, tin cảm ơn, tin nhắc nhở\r\n *\r\n * 4. TIN NHẮN HỆ THỐNG:\r\n *    - Tin nhắn thông báo từ hệ thống\r\n *    - Bao gồm: thông báo bảo trì, cập nhật chính sách\r\n */\r\nexport class GeneralMessageService {\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    // Message endpoints\r\n    message: {\r\n      send: \"https://openapi.zalo.me/v3.0/oa/message/cs\",\r\n      sendAnonymous: \"https://openapi.zalo.me/v3.0/oa/message/anonymous\",\r\n    },\r\n\r\n  };\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n\r\n\r\n  /**\r\n   * Gửi tin nhắn phản hồi hình ảnh\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param message Nội dung tin nhắn hình ảnh\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   */\r\n  public async sendResponseImageMessage(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    message: ImageMessage\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      if (!message.attachment?.payload?.url) {\r\n        throw new ZaloSDKError(\"URL hình ảnh không được để trống\", -1);\r\n      }\r\n\r\n      const endpoint = this.endpoints.message.send;\r\n      const request: SendMessageRequest = {\r\n        recipient,\r\n        message,\r\n        messaging_type: \"response\",\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send response image message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send response image message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn ẩn danh\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param message Nội dung tin nhắn ẩn danh\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   */\r\n  public async sendAnonymousMessage(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    message: AnonymousTextMessage\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      if (!message.text || message.text.trim().length === 0) {\r\n        throw new ZaloSDKError(\r\n          \"Nội dung tin nhắn ẩn danh không được để trống\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      const endpoint = this.endpoints.message.sendAnonymous;\r\n      const request = {\r\n        recipient,\r\n        message,\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send anonymous message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send anonymous message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n\r\n\r\n\r\n\r\n  /**\r\n   * Gửi tin nhắn tổng quát\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận\r\n   * @param message Nội dung tin nhắn\r\n   * @param messagingType Loại tin nhắn (response, update, message_tag)\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   */\r\n  public async sendMessage(\r\n    accessToken: string,\r\n    recipient: MessageRecipient,\r\n    message: Message,\r\n    messagingType:\r\n      | \"consultation\"\r\n      | \"transaction\"\r\n      | \"promotion\"\r\n      | \"response\"\r\n      | \"update\"\r\n      | \"message_tag\" = \"response\"\r\n  ): Promise<SendMessageResponse> {\r\n    try {\r\n      const endpoint = this.endpoints.message.send;\r\n      const request: SendMessageRequest = {\r\n        recipient,\r\n        message,\r\n        messaging_type: messagingType,\r\n      };\r\n\r\n      const result: ZaloResponse<SendMessageResponse> =\r\n        await this.client.apiPost(endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\n\r\n/**\r\n * Interface cho thông tin quota tin nhắn\r\n */\r\nexport interface MessageQuotaInfo {\r\n  quota: number;\r\n  used: number;\r\n  remaining: number;\r\n  reset_time: number;\r\n}\r\n\r\n/**\r\n * Interface cho tin nhắn trong cuộc hội thoại - matches Zalo API docs exactly\r\n */\r\nexport interface ConversationMessage {\r\n  /** ID của tin nhắn */\r\n  message_id: string;\r\n  /** Thuộc tính cho biết tin nhắn được gửi từ OA hoặc người dùng. 0: tin nhắn từ OA đến user, 1: tin nhắn từ user đến OA */\r\n  src: number;\r\n  /** Thời gian tin nhắn được gửi (định dạng timestamp) */\r\n  time: number;\r\n  /** Loại tin nhắn: text, voice, photo, GIF, link, links, sticker, location */\r\n  type: string;\r\n  /** Nội dung tin nhắn */\r\n  message: string;\r\n  /** Thuộc tính trả về 1 danh sách các đường dẫn được đính kèm trong tin nhắn (chỉ có khi type là link hoặc links) */\r\n  links?: string[];\r\n  /** URL của ảnh hoặc GIF (chỉ có khi type là GIF hoặc photo) */\r\n  thumb?: string;\r\n  /** Đường dẫn đến audio/ảnh/GIF/nhãn dán */\r\n  url?: string;\r\n  /** Mô tả của ảnh (chỉ có khi type là photo) */\r\n  description?: string;\r\n  /** ID của đối tượng gửi tin nhắn (có thể là user_id hoặc oa_id) */\r\n  from_id: string;\r\n  /** ID của đối tượng nhận tin nhắn (có thể là user_id hoặc oa_id) */\r\n  to_id: string;\r\n  /** Tên hiển thị của đối tượng gửi tin nhắn */\r\n  from_display_name: string;\r\n  /** Đường dẫn đến ảnh đại diện của đối tượng gửi tin nhắn */\r\n  from_avatar: string;\r\n  /** Tên hiển thị của đối tượng nhận tin nhắn */\r\n  to_display_name: string;\r\n  /** Đường dẫn đến ảnh đại diện của đối tượng nhận tin nhắn */\r\n  to_avatar: string;\r\n  /** Thuộc tính trả về giá trị longitude và latitude của người dùng (chỉ có khi type là location) */\r\n  location?: string;\r\n}\r\n\r\n/**\r\n * Interface cho cuộc hội thoại\r\n */\r\nexport interface Conversation {\r\n  conversation_id: string;\r\n  user_id: string;\r\n  last_message: ConversationMessage;\r\n  unread_count: number;\r\n  updated_time: number;\r\n}\r\n\r\n/**\r\n * Interface cho kết quả upload file - matches Zalo API docs exactly\r\n */\r\nexport interface UploadFileResult {\r\n  /** Token của file, sử dụng cho API gửi thông báo đính kèm file */\r\n  token: string;\r\n}\r\n\r\n/**\r\n * Interface cho kết quả upload image - matches Zalo API docs exactly\r\n */\r\nexport interface UploadImageResult {\r\n  /** ID của ảnh được upload */\r\n  attachment_id: string;\r\n}\r\n\r\n\r\n/**\r\n * Service xử lý các API quản lý tin nhắn của Zalo Official Account\r\n *\r\n * Bao gồm các chức năng:\r\n * - Kiểm tra hạn mức gửi tin nhắn\r\n * - Lấy danh sách tin nhắn trong cuộc hội thoại với user cụ thể\r\n * - Lấy thông tin tin nhắn gần nhất\r\n * - Upload file và hình ảnh\r\n *\r\n * ĐIỀU KIỆN SỮCDNG:\r\n *\r\n * 1. KIỂM TRA HẠN MỨC:\r\n *    - Cần quyền truy cập thông tin quota từ Zalo\r\n *    - Quota được reset hàng ngày vào 00:00 GMT+7\r\n *\r\n * 2. LẤY TIN NHẮN:\r\n *    - API hỗ trợ lấy thông tin tin nhắn giữa OA và một người dùng cụ thể\r\n *    - Mỗi request lấy tối đa được 10 tin nhắn\r\n *    - Tin nhắn gần nhất có thứ tự là 0\r\n *\r\n * 3. UPLOAD FILE:\r\n *    - Kích thước tối đa 5MB\r\n *    - Hỗ trợ các định dạng: PDF, DOC, DOCX\r\n *    - Quota: 5000 request/tháng\r\n *\r\n * 4. UPLOAD HÌNH ẢNH:\r\n *    - Dung lượng tối đa 1MB\r\n *    - Hỗ trợ các định dạng: JPG và PNG\r\n *    - Ảnh sẽ được lưu trên server tối đa 7 ngày\r\n *    - Quota: 5000 request/tháng\r\n *\r\n * 5. UPLOAD ẢNH GIF:\r\n *    - Kích thước tối đa 5MB\r\n *    - Chỉ hỗ trợ định dạng GIF\r\n *    - Ảnh GIF được lưu trên server tối đa 7 ngày\r\n *    - Quota: 5000 request/tháng\r\n */\r\nexport class MessageManagementService {\r\n  // Zalo API endpoints - organized by functionality\r\n  private readonly endpoints = {\r\n    // Message management endpoints\r\n    message: {\r\n      quota: \"https://openapi.zalo.me/v2.0/oa/quota\",\r\n      conversation: \"https://openapi.zalo.me/v2.0/oa/conversation\",\r\n      recentConversations: \"https://openapi.zalo.me/v2.0/oa/listrecentchat\",\r\n    },\r\n    // Upload endpoints\r\n    upload: {\r\n      file: \"https://openapi.zalo.me/v2.0/oa/upload/file\",\r\n      image: \"https://openapi.zalo.me/v2.0/oa/upload/image\",\r\n      gif: \"https://openapi.zalo.me/v2.0/oa/upload/gif\",\r\n    },\r\n  };\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Kiểm tra hạn mức gửi tin nhắn đến user cụ thể\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người dùng Zalo\r\n   * @returns Thông tin hạn mức gửi tin nhắn\r\n   */\r\n  async checkMessageQuota(\r\n    accessToken: string,\r\n    userId: string\r\n  ): Promise<MessageQuotaInfo> {\r\n    try {\r\n      const result: ZaloResponse<MessageQuotaInfo> = await this.client.apiGet(\r\n        this.endpoints.message.quota,\r\n        accessToken,\r\n        { user_id: userId }\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to check message quota\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No quota data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to check message quota: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Lấy danh sách tin nhắn trong cuộc hội thoại với user\r\n   *\r\n   * API hỗ trợ lấy thông tin tin nhắn giữa OA và một người dùng cụ thể.\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người dùng Zalo cần lấy danh sách hội thoại\r\n   * @param offset Thứ tự của tin nhắn đầu tiên trong danh sách trả về (tin nhắn gần nhất có thứ tự là 0)\r\n   * @param count Số lượng tin nhắn cần lấy (mỗi request lấy tối đa 10 tin nhắn)\r\n   * @returns Danh sách tin nhắn\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const messages = await messageService.getConversationMessages(\r\n   *   accessToken,\r\n   *   '2512523625412515',\r\n   *   0,\r\n   *   5\r\n   * );\r\n   * ```\r\n   */\r\n  async getConversationMessages(\r\n    accessToken: string,\r\n    userId: string,\r\n    offset: number = 0,\r\n    count: number = 10\r\n  ): Promise<ConversationMessage[]> {\r\n    try {\r\n      // Validate count theo docs: mỗi request lấy tối đa 10 tin nhắn\r\n      if (count > 10) {\r\n        throw new ZaloSDKError(\"Mỗi request lấy tối đa được 10 tin nhắn\", -1);\r\n      }\r\n\r\n      // Theo docs Zalo: params phải được wrap trong object data dạng JSON string\r\n      const params = {\r\n        data: JSON.stringify({\r\n          user_id: userId,\r\n          offset,\r\n          count,\r\n        }),\r\n      };\r\n\r\n      const result: ZaloResponse<ConversationMessage[]> = await this.client.apiGet(\r\n        this.endpoints.message.conversation,\r\n        accessToken,\r\n        params\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get conversation messages\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No conversation data received\", -1);\r\n      }\r\n\r\n      // Response trả về mảng trực tiếp trong data\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get conversation messages: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Lấy danh sách tin nhắn trong cuộc hội thoại với user (sử dụng POST method)\r\n   *\r\n   * API hỗ trợ lấy thông tin tin nhắn giữa OA và một người dùng cụ thể sử dụng phương thức POST.\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người dùng Zalo cần lấy danh sách hội thoại\r\n   * @param offset Thứ tự của tin nhắn đầu tiên trong danh sách trả về (tin nhắn gần nhất có thứ tự là 0)\r\n   * @param count Số lượng tin nhắn cần lấy (mỗi request lấy tối đa 10 tin nhắn)\r\n   * @returns Danh sách tin nhắn\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const messages = await messageService.postConversationMessages(\r\n   *   accessToken,\r\n   *   '2512523625412515',\r\n   *   0,\r\n   *   5\r\n   * );\r\n   * ```\r\n   */\r\n  async postConversationMessages(\r\n    accessToken: string,\r\n    userId: string,\r\n    offset: number = 0,\r\n    count: number = 10\r\n  ): Promise<ConversationMessage[]> {\r\n    try {\r\n      // Validate count theo docs: mỗi request lấy tối đa 10 tin nhắn\r\n      if (count > 10) {\r\n        throw new ZaloSDKError(\"Mỗi request lấy tối đa được 10 tin nhắn\", -1);\r\n      }\r\n\r\n      // Body format for POST request - gửi trực tiếp các tham số\r\n      const body = {\r\n        user_id: userId,\r\n        offset,\r\n        count,\r\n      };\r\n\r\n      const result: ZaloResponse<ConversationMessage[]> = await this.client.apiPost(\r\n        this.endpoints.message.conversation,\r\n        accessToken,\r\n        body\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get conversation messages\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No conversation data received\", -1);\r\n      }\r\n\r\n      // Response trả về mảng trực tiếp trong data\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get conversation messages: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Lấy thông tin tin nhắn gần nhất\r\n   * \r\n   * API hỗ trợ lấy thông tin tối đa 10 tin nhắn gần nhất giữa OA và người dùng.\r\n   * \r\n   * @param accessToken Access token của Official Account\r\n   * @param offset Thứ tự của tin nhắn đầu tiên trong danh sách trả về (tin nhắn gần nhất có thứ tự là 0)\r\n   * @param count Số lượng tin nhắn muốn lấy (mỗi request lấy tối đa 10 tin nhắn)\r\n   * @returns Danh sách tin nhắn gần nhất\r\n   * \r\n   * @example\r\n   * ```typescript\r\n   * const recentMessages = await messageService.getRecentConversations(\r\n   *   accessToken,\r\n   *   0,\r\n   *   5\r\n   * );\r\n   * ```\r\n   */\r\n  async getRecentConversations(\r\n    accessToken: string,\r\n    offset: number = 0,\r\n    count: number = 10\r\n  ): Promise<ConversationMessage[]> {\r\n    try {\r\n      // Validate count theo docs: mỗi request lấy tối đa 10 tin nhắn\r\n      if (count > 10) {\r\n        throw new ZaloSDKError(\"Mỗi request lấy tối đa 10 tin nhắn\", -1);\r\n      }\r\n\r\n      // Theo docs Zalo: params phải được wrap trong object data dạng JSON string\r\n      const params = {\r\n        data: JSON.stringify({\r\n          offset,\r\n          count,\r\n        }),\r\n      };\r\n\r\n      const result: ZaloResponse<ConversationMessage[]> = await this.client.apiGet(\r\n        this.endpoints.message.recentConversations,\r\n        accessToken,\r\n        params\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to get recent messages\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No recent messages data received\", -1);\r\n      }\r\n\r\n      // Response trả về mảng trực tiếp trong data\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to get recent messages: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Upload file để sử dụng trong tin nhắn\r\n   * @param accessToken Access token của Official Account\r\n   * @param fileData Dữ liệu file (base64 hoặc buffer)\r\n   * @param fileName Tên file\r\n   * @returns Token của file đã upload\r\n   *\r\n   * Lưu ý:\r\n   * - Chỉ hỗ trợ file PDF/DOC/DOCX\r\n   * - Dung lượng file không vượt quá 5MB\r\n   * - File sẽ được lưu trên server tối đa 7 ngày\r\n   * - Quota: 5000 request/tháng\r\n   */\r\n  async uploadFile(\r\n    accessToken: string,\r\n    fileData: string | Buffer,\r\n    fileName: string\r\n  ): Promise<UploadFileResult> {\r\n    try {\r\n      // Validate file format (only PDF/DOC/DOCX)\r\n      const allowedExtensions = ['.pdf', '.doc', '.docx'];\r\n      const fileExt = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));\r\n      if (!allowedExtensions.includes(fileExt)) {\r\n        throw new ZaloSDKError(\"Chỉ hỗ trợ file PDF, DOC, DOCX\", -1);\r\n      }\r\n\r\n      // Validate file size (max 5MB)\r\n      const maxSize = 5 * 1024 * 1024; // 5MB\r\n      let fileSize: number;\r\n\r\n      if (typeof fileData === \"string\") {\r\n        // Base64 string\r\n        fileSize = Buffer.from(fileData, \"base64\").length;\r\n      } else {\r\n        // Buffer\r\n        fileSize = fileData.length;\r\n      }\r\n\r\n      if (fileSize > maxSize) {\r\n        throw new ZaloSDKError(\"Kích thước file không được vượt quá 5MB\", -1);\r\n      }\r\n\r\n      let buffer: Buffer;\r\n      if (typeof fileData === \"string\") {\r\n        buffer = Buffer.from(fileData, \"base64\");\r\n      } else {\r\n        buffer = fileData;\r\n      }\r\n\r\n      const result: ZaloResponse<UploadFileResult> = await this.client.apiUploadFile(\r\n        this.endpoints.upload.file,\r\n        accessToken,\r\n        buffer,\r\n        fileName\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to upload file\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No upload result received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to upload file: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Upload hình ảnh để sử dụng trong tin nhắn\r\n   * @param accessToken Access token của Official Account\r\n   * @param imageData Dữ liệu hình ảnh (base64 hoặc buffer)\r\n   * @param fileName Tên file hình ảnh\r\n   * @returns ID của ảnh đã upload\r\n   *\r\n   * Lưu ý:\r\n   * - Hỗ trợ các định dạng: JPG và PNG\r\n   * - Dung lượng tối đa: 1MB\r\n   * - Ảnh sẽ được lưu trên server tối đa 7 ngày\r\n   * - Quota: 5000 request/tháng\r\n   */\r\n  async uploadImage(\r\n    accessToken: string,\r\n    imageData: string | Buffer,\r\n    fileName: string\r\n  ): Promise<UploadImageResult> {\r\n    try {\r\n      // Validate image size (max 1MB)\r\n      const maxSize = 1 * 1024 * 1024; // 1MB\r\n      let imageSize: number;\r\n\r\n      if (typeof imageData === \"string\") {\r\n        // Base64 string\r\n        imageSize = Buffer.from(imageData, \"base64\").length;\r\n      } else {\r\n        // Buffer\r\n        imageSize = imageData.length;\r\n      }\r\n\r\n      if (imageSize > maxSize) {\r\n        throw new ZaloSDKError(\r\n          \"Kích thước hình ảnh không được vượt quá 1MB\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      let buffer: Buffer;\r\n      if (typeof imageData === \"string\") {\r\n        buffer = Buffer.from(imageData, \"base64\");\r\n      } else {\r\n        buffer = imageData;\r\n      }\r\n\r\n      const result: ZaloResponse<UploadImageResult> = await this.client.apiUploadFile(\r\n        this.endpoints.upload.image,\r\n        accessToken,\r\n        buffer,\r\n        fileName\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to upload image\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No upload result received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to upload image: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Upload ảnh GIF để sử dụng trong tin nhắn\r\n   * @param accessToken Access token của Official Account\r\n   * @param gifData Dữ liệu ảnh GIF (base64 hoặc buffer)\r\n   * @param fileName Tên file ảnh GIF\r\n   * @returns Thông tin ảnh GIF đã upload\r\n   *\r\n   * Lưu ý:\r\n   * - Dung lượng tối đa: 5MB\r\n   * - Ảnh GIF sẽ được lưu trên server tối đa 7 ngày\r\n   * - Quota: 5000 request/tháng\r\n   * - Định dạng hỗ trợ: GIF\r\n   */\r\n  async uploadGif(\r\n    accessToken: string,\r\n    gifData: string | Buffer,\r\n    fileName: string\r\n  ): Promise<UploadImageResult> {\r\n    try {\r\n      // Validate GIF size (max 5MB)\r\n      const maxSize = 5 * 1024 * 1024; // 5MB\r\n      let gifSize: number;\r\n\r\n      if (typeof gifData === \"string\") {\r\n        // Base64 string\r\n        gifSize = Buffer.from(gifData, \"base64\").length;\r\n      } else {\r\n        // Buffer\r\n        gifSize = gifData.length;\r\n      }\r\n\r\n      if (gifSize > maxSize) {\r\n        throw new ZaloSDKError(\r\n          \"Kích thước ảnh GIF không được vượt quá 5MB\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      // Validate file extension\r\n      if (!fileName.toLowerCase().endsWith(\".gif\")) {\r\n        throw new ZaloSDKError(\"File phải có định dạng GIF\", -1);\r\n      }\r\n\r\n      let buffer: Buffer;\r\n      if (typeof gifData === \"string\") {\r\n        buffer = Buffer.from(gifData, \"base64\");\r\n      } else {\r\n        buffer = gifData;\r\n      }\r\n\r\n      const result: ZaloResponse<UploadImageResult> = await this.client.apiUploadFile(\r\n        this.endpoints.upload.gif,\r\n        accessToken,\r\n        buffer,\r\n        fileName\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to upload GIF\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No upload result received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to upload GIF: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n}\r\n","/**\r\n * Broadcast Service for Zalo Official Account\r\n *\r\n * ⚠️ **DEPRECATED**: Broadcast Message API (v2.0) đã bị deprecated bởi Zalo\r\n *\r\n * **Khuyến nghị**: Sử dụng **ZNS Service** thay thế:\r\n * - `ZNSService.sendMessageByUid()` - Gửi tin nhắn template theo UID\r\n * - `ZNSService.sendMessage()` - Gửi tin nhắn theo số điện thoại\r\n * - `ZNSService.sendHashPhoneMessage()` - Gửi tin nhắn theo hash phone\r\n *\r\n * Tham khảo: https://developers.zalo.me/docs/zalo-notification-service/\r\n *\r\n * ===\r\n *\r\n * Gửi tin Truyền thông Broadcast đến nhiều người dùng dựa trên tiêu chí targeting\r\n * API: https://openapi.zalo.me/v2.0/oa/message\r\n *\r\n * TÍNH NĂNG:\r\n * 1. GỬI TIN BROADCAST:\r\n *    - Gửi tin nhắn đến nhiều người dùng cùng lúc\r\n *    - Targeting theo tuổi, giới tính, địa điểm, platform\r\n *    - Chỉ hỗ trợ template type \"media\" với article attachment\r\n *\r\n * 2. TARGETING CRITERIA:\r\n *    - Ages: Nhóm tuổi (0-12, 13-17, 18-24, 25-34, 35-44, 45-54, 55-64, 65+)\r\n *    - Gender: Giới tính (All, Male, Female)\r\n *    - Cities: Tỉnh thành cụ thể (63 tỉnh thành Việt Nam)\r\n *    - Locations: Miền (Bắc, Trung, Nam)\r\n *    - Platform: Hệ điều hành (iOS, Android, Windows Phone)\r\n *\r\n * 3. GIỚI HẠN:\r\n *    - Chỉ gửi được article attachment\r\n *    - Cần có quyền broadcast từ Zalo\r\n *    - Tuân thủ chính sách spam và nội dung\r\n *\r\n * LỖI THƯỜNG GẶP:\r\n * - 3001: Không có quyền gửi broadcast\r\n * - 3002: Article attachment không tồn tại\r\n * - 3003: Targeting criteria không hợp lệ\r\n * - 3004: Vượt quá giới hạn broadcast\r\n * - 3005: Nội dung vi phạm chính sách\r\n *\r\n * @deprecated Sử dụng ZNSService thay vì BroadcastService\r\n */\r\n\r\nimport { ZaloClient } from '../clients/zalo-client';\r\nimport { ZaloResponse, ZaloSDKError } from '../types/common';\r\nimport {\r\n  BroadcastRequest,\r\n  BroadcastResponse,\r\n  BroadcastTarget,\r\n  BroadcastRecipient,\r\n  BroadcastMessage,\r\n  BROADCAST_CITY_CODES,\r\n  BROADCAST_AGE_CODES,\r\n  BROADCAST_GENDER_CODES,\r\n  BROADCAST_LOCATION_CODES,\r\n  BROADCAST_PLATFORM_CODES,\r\n  MultipleBroadcastResult,\r\n  MultipleBroadcastProgress,\r\n  BroadcastMessageResult\r\n} from '../types/broadcast';\r\n\r\nexport class BroadcastService {\r\n  // Zalo API endpoint for broadcast messages\r\n  private readonly broadcastApiUrl = \"https://openapi.zalo.me/v2.0/oa/message\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Gửi tin nhắn broadcast đến nhiều người dùng\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin targeting người nhận\r\n   * @param attachmentId ID của article attachment\r\n   * @returns Thông tin tin nhắn broadcast đã gửi\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendBroadcastMessage(\r\n    accessToken: string,\r\n    recipient: BroadcastRecipient,\r\n    attachmentId: string\r\n  ): Promise<BroadcastResponse> {\r\n    try {\r\n      // Validate input parameters\r\n      this.validateBroadcastRequest(recipient, attachmentId);\r\n\r\n      // Build broadcast message\r\n      const message: BroadcastMessage = {\r\n        attachment: {\r\n          type: \"template\",\r\n          payload: {\r\n            template_type: \"media\",\r\n            elements: [\r\n              {\r\n                media_type: \"article\",\r\n                attachment_id: attachmentId\r\n              }\r\n            ]\r\n          }\r\n        }\r\n      };\r\n\r\n      // Build request\r\n      const request: BroadcastRequest = {\r\n        recipient,\r\n        message\r\n      };\r\n\r\n      // Send broadcast request\r\n      const result: ZaloResponse<BroadcastResponse> = await this.client.apiPost(\r\n        this.broadcastApiUrl,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send broadcast message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No broadcast response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send broadcast message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Tạo broadcast target với các tiêu chí targeting\r\n   * @param criteria Các tiêu chí targeting\r\n   * @returns BroadcastTarget object\r\n   */\r\n  public createBroadcastTarget(criteria: {\r\n    ages?: string[];\r\n    gender?: \"ALL\" | \"MALE\" | \"FEMALE\";\r\n    cities?: string[];\r\n    locations?: (\"NORTH\" | \"CENTRAL\" | \"SOUTH\")[];\r\n    platforms?: (\"IOS\" | \"ANDROID\" | \"WINDOWS_PHONE\")[];\r\n  }): BroadcastTarget {\r\n    const target: BroadcastTarget = {};\r\n\r\n    // Convert ages to comma-separated string\r\n    if (criteria.ages && criteria.ages.length > 0) {\r\n      const ageCodes = criteria.ages.map(age => {\r\n        const code = BROADCAST_AGE_CODES[age as keyof typeof BROADCAST_AGE_CODES];\r\n        if (!code) {\r\n          throw new ZaloSDKError(`Invalid age group: ${age}`, -1);\r\n        }\r\n        return code;\r\n      });\r\n      target.ages = ageCodes.join(\",\");\r\n    }\r\n\r\n    // Convert gender\r\n    if (criteria.gender) {\r\n      const genderCode = BROADCAST_GENDER_CODES[criteria.gender];\r\n      if (!genderCode) {\r\n        throw new ZaloSDKError(`Invalid gender: ${criteria.gender}`, -1);\r\n      }\r\n      target.gender = genderCode;\r\n    }\r\n\r\n    // Convert cities to comma-separated string\r\n    if (criteria.cities && criteria.cities.length > 0) {\r\n      const cityCodes = criteria.cities.map(city => {\r\n        const code = BROADCAST_CITY_CODES[city as keyof typeof BROADCAST_CITY_CODES];\r\n        if (!code) {\r\n          throw new ZaloSDKError(`Invalid city: ${city}`, -1);\r\n        }\r\n        return code;\r\n      });\r\n      target.cities = cityCodes.join(\",\");\r\n    }\r\n\r\n    // Convert locations to comma-separated string\r\n    if (criteria.locations && criteria.locations.length > 0) {\r\n      const locationCodes = criteria.locations.map(location => {\r\n        const code = BROADCAST_LOCATION_CODES[location];\r\n        if (!code) {\r\n          throw new ZaloSDKError(`Invalid location: ${location}`, -1);\r\n        }\r\n        return code;\r\n      });\r\n      target.locations = locationCodes.join(\",\");\r\n    }\r\n\r\n    // Convert platforms to comma-separated string\r\n    if (criteria.platforms && criteria.platforms.length > 0) {\r\n      const platformCodes = criteria.platforms.map(platform => {\r\n        const code = BROADCAST_PLATFORM_CODES[platform];\r\n        if (!code) {\r\n          throw new ZaloSDKError(`Invalid platform: ${platform}`, -1);\r\n        }\r\n        return code;\r\n      });\r\n      target.platform = platformCodes.join(\",\");\r\n    }\r\n\r\n    return target;\r\n  }\r\n\r\n  /**\r\n   * Validate broadcast request parameters\r\n   * @param recipient Broadcast recipient\r\n   * @param attachmentId Article attachment ID\r\n   */\r\n  private validateBroadcastRequest(\r\n    recipient: BroadcastRecipient,\r\n    attachmentId: string\r\n  ): void {\r\n    // Validate recipient\r\n    if (!recipient || !recipient.target) {\r\n      throw new ZaloSDKError(\"Recipient target is required for broadcast\", -1);\r\n    }\r\n\r\n    // Validate attachment ID\r\n    if (!attachmentId || attachmentId.trim().length === 0) {\r\n      throw new ZaloSDKError(\"Article attachment ID is required\", -1);\r\n    }\r\n\r\n    // Validate target has at least one criteria\r\n    const target = recipient.target;\r\n    const hasTargeting = target.ages || target.gender || target.cities || \r\n                        target.locations || target.platform;\r\n    \r\n    if (!hasTargeting) {\r\n      throw new ZaloSDKError(\r\n        \"At least one targeting criteria is required (ages, gender, cities, locations, or platform)\",\r\n        -1\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get available city codes for targeting\r\n   * @returns Object mapping city names to codes\r\n   */\r\n  public getCityCodes(): typeof BROADCAST_CITY_CODES {\r\n    return BROADCAST_CITY_CODES;\r\n  }\r\n\r\n  /**\r\n   * Get available age group codes for targeting\r\n   * @returns Object mapping age groups to codes\r\n   */\r\n  public getAgeGroupCodes(): typeof BROADCAST_AGE_CODES {\r\n    return BROADCAST_AGE_CODES;\r\n  }\r\n\r\n  /**\r\n   * Get available gender codes for targeting\r\n   * @returns Object mapping genders to codes\r\n   */\r\n  public getGenderCodes(): typeof BROADCAST_GENDER_CODES {\r\n    return BROADCAST_GENDER_CODES;\r\n  }\r\n\r\n  /**\r\n   * Get available location codes for targeting\r\n   * @returns Object mapping locations to codes\r\n   */\r\n  public getLocationCodes(): typeof BROADCAST_LOCATION_CODES {\r\n    return BROADCAST_LOCATION_CODES;\r\n  }\r\n\r\n  /**\r\n   * Get available platform codes for targeting\r\n   * @returns Object mapping platforms to codes\r\n   */\r\n  public getPlatformCodes(): typeof BROADCAST_PLATFORM_CODES {\r\n    return BROADCAST_PLATFORM_CODES;\r\n  }\r\n\r\n  /**\r\n   * Gửi nhiều tin nhắn broadcast với nhiều attachment IDs\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin targeting người nhận\r\n   * @param attachmentIds Danh sách các article attachment IDs\r\n   * @param options Tùy chọn gửi (delay giữa các tin, parallel/sequential)\r\n   * @returns Danh sách kết quả gửi broadcast\r\n   * @deprecated Sử dụng ZNSService.sendMessageByUid() thay vì method này\r\n   */\r\n  public async sendMultipleBroadcastMessages(\r\n    accessToken: string,\r\n    recipient: BroadcastRecipient,\r\n    attachmentIds: string[],\r\n    options?: {\r\n      delay?: number; // Delay giữa các tin nhắn (ms)\r\n      mode?: 'parallel' | 'sequential'; // Gửi song song hay tuần tự\r\n      onProgress?: (progress: MultipleBroadcastProgress) => void; // Callback theo dõi tiến độ\r\n    }\r\n  ): Promise<MultipleBroadcastResult> {\r\n    try {\r\n      // Validate input\r\n      if (!attachmentIds || attachmentIds.length === 0) {\r\n        throw new ZaloSDKError(\"At least one attachment ID is required\", -1);\r\n      }\r\n\r\n      // Remove duplicates\r\n      const uniqueAttachmentIds = [...new Set(attachmentIds)];\r\n\r\n      const startTime = Date.now();\r\n      const results: BroadcastMessageResult[] = [];\r\n      const mode = options?.mode || 'sequential';\r\n      const delay = options?.delay || 0;\r\n\r\n      if (mode === 'parallel') {\r\n        // Gửi song song tất cả\r\n        const promises = uniqueAttachmentIds.map(async (attachmentId, index) => {\r\n          try {\r\n            const result = await this.sendBroadcastMessage(\r\n              accessToken,\r\n              recipient,\r\n              attachmentId\r\n            );\r\n\r\n            const messageResult: BroadcastMessageResult = {\r\n              attachmentId,\r\n              success: true,\r\n              messageId: result.data.message_id,\r\n              error: null,\r\n              sentAt: new Date()\r\n            };\r\n\r\n            // Report progress\r\n            if (options?.onProgress) {\r\n              options.onProgress({\r\n                total: uniqueAttachmentIds.length,\r\n                completed: index + 1,\r\n                successful: results.filter(r => r.success).length + 1,\r\n                failed: results.filter(r => !r.success).length,\r\n                currentAttachmentId: attachmentId,\r\n                isCompleted: index === uniqueAttachmentIds.length - 1\r\n              });\r\n            }\r\n\r\n            return messageResult;\r\n          } catch (error) {\r\n            const messageResult: BroadcastMessageResult = {\r\n              attachmentId,\r\n              success: false,\r\n              messageId: null,\r\n              error: error instanceof ZaloSDKError ? error : new ZaloSDKError(error.message, -1),\r\n              sentAt: new Date()\r\n            };\r\n\r\n            // Report progress\r\n            if (options?.onProgress) {\r\n              options.onProgress({\r\n                total: uniqueAttachmentIds.length,\r\n                completed: index + 1,\r\n                successful: results.filter(r => r.success).length,\r\n                failed: results.filter(r => !r.success).length + 1,\r\n                currentAttachmentId: attachmentId,\r\n                isCompleted: index === uniqueAttachmentIds.length - 1\r\n              });\r\n            }\r\n\r\n            return messageResult;\r\n          }\r\n        });\r\n\r\n        const parallelResults = await Promise.all(promises);\r\n        results.push(...parallelResults);\r\n\r\n      } else {\r\n        // Gửi tuần tự\r\n        for (let i = 0; i < uniqueAttachmentIds.length; i++) {\r\n          const attachmentId = uniqueAttachmentIds[i];\r\n\r\n          try {\r\n            const result = await this.sendBroadcastMessage(\r\n              accessToken,\r\n              recipient,\r\n              attachmentId\r\n            );\r\n\r\n            const messageResult: BroadcastMessageResult = {\r\n              attachmentId,\r\n              success: true,\r\n              messageId: result.data.message_id,\r\n              error: null,\r\n              sentAt: new Date()\r\n            };\r\n\r\n            results.push(messageResult);\r\n\r\n            // Report progress\r\n            if (options?.onProgress) {\r\n              options.onProgress({\r\n                total: uniqueAttachmentIds.length,\r\n                completed: i + 1,\r\n                successful: results.filter(r => r.success).length,\r\n                failed: results.filter(r => !r.success).length,\r\n                currentAttachmentId: attachmentId,\r\n                isCompleted: i === uniqueAttachmentIds.length - 1\r\n              });\r\n            }\r\n\r\n          } catch (error) {\r\n            const messageResult: BroadcastMessageResult = {\r\n              attachmentId,\r\n              success: false,\r\n              messageId: null,\r\n              error: error instanceof ZaloSDKError ? error : new ZaloSDKError(error.message, -1),\r\n              sentAt: new Date()\r\n            };\r\n\r\n            results.push(messageResult);\r\n\r\n            // Report progress\r\n            if (options?.onProgress) {\r\n              options.onProgress({\r\n                total: uniqueAttachmentIds.length,\r\n                completed: i + 1,\r\n                successful: results.filter(r => r.success).length,\r\n                failed: results.filter(r => !r.success).length,\r\n                currentAttachmentId: attachmentId,\r\n                isCompleted: i === uniqueAttachmentIds.length - 1\r\n              });\r\n            }\r\n          }\r\n\r\n          // Delay before next message (except for last one)\r\n          if (delay > 0 && i < uniqueAttachmentIds.length - 1) {\r\n            await this.sleep(delay);\r\n          }\r\n        }\r\n      }\r\n\r\n      const totalDuration = Date.now() - startTime;\r\n      const successful = results.filter(r => r.success).length;\r\n      const failed = results.filter(r => !r.success).length;\r\n\r\n      return {\r\n        totalMessages: uniqueAttachmentIds.length,\r\n        successfulMessages: successful,\r\n        failedMessages: failed,\r\n        results,\r\n        totalDuration,\r\n        mode,\r\n        targetCriteria: recipient.target\r\n      };\r\n\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send multiple broadcast messages: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Sleep utility for delays\r\n   * @param ms Milliseconds to sleep\r\n   */\r\n  private sleep(ms: number): Promise<void> {\r\n    return new Promise(resolve => setTimeout(resolve, ms));\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\nimport {\r\n  CreateOrderRequest,\r\n  CreateOrderResponse,\r\n  OrderData,\r\n  ConfirmOrderRequest,\r\n  ConfirmOrderResponse,\r\n  ConfirmedOrderData,\r\n  isProductOrderRequest,\r\n  isRedeemOrderRequest,\r\n  PurchaseErrorCode,\r\n  OA_PRODUCTS,\r\n  PRODUCT_INFO,\r\n  ProductInfo,\r\n} from \"../types/purchase\";\r\n\r\n/**\r\n * Service xử lý các API mua sản phẩm/dịch vụ OA của Zalo\r\n *\r\n * ĐIỀU KIỆN SỬ DỤNG PURCHASE API:\r\n *\r\n * 1. QUYỀN TRUY CẬP:\r\n *    - Ứng dụng cần được cấp quyền quản lý \"Mua sản phẩm dịch vụ OA\"\r\n *    - OA phải được xác minh và có quyền bán sản phẩm/dịch vụ\r\n *\r\n * 2. THỜI GIAN TẠO ĐỚN:\r\n *    - Order chỉ được tạo từ 00:01 đến 23:54 hàng ngày\r\n *    - Không thể tạo đơn hàng trong khung giờ bảo trì (23:55 - 00:00)\r\n *\r\n * 3. SẢN PHẨM/DỊCH VỤ:\r\n *    - Sản phẩm phải có sẵn và được phép bán\r\n *    - Đối tượng thụ hưởng (beneficiary) phải phù hợp với sản phẩm\r\n *    - Có thể sử dụng product_id HOẶC redeem_code, không được dùng cả hai\r\n *\r\n * 4. THANH TOÁN:\r\n *    - Tài khoản ZCA phải có đủ số dư để thanh toán\r\n *    - Voucher code (nếu có) phải hợp lệ và chưa hết hạn\r\n *\r\n * 5. XÁC THỰC:\r\n *    - Mỗi đơn hàng sẽ có verified_token (OTT) để xác nhận thanh toán\r\n *    - OTT có hiệu lực trong vòng 5 phút kể từ khi tạo đơn\r\n *\r\n * LỖI THƯỜNG GẶP:\r\n * - 1001: Beneficiary không hợp lệ\r\n * - 1002: Product ID không tồn tại hoặc không khả dụng\r\n * - 1003: Redeem code không hợp lệ hoặc đã được sử dụng\r\n * - 1004: Voucher code không hợp lệ hoặc đã hết hạn\r\n * - 1005: Sản phẩm không khả dụng cho đối tượng thụ hưởng\r\n * - 1006: Số dư tài khoản không đủ\r\n * - 1007: Ngoài thời gian cho phép tạo đơn hàng\r\n * - 1008: Đơn hàng trùng lặp\r\n * - 1009: Không có quyền truy cập\r\n */\r\nexport class PurchaseService {\r\n  private readonly purchaseApiUrl = \"https://openapi.zalo.me/v3.0/oa/purchase\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Tạo đơn hàng mua sản phẩm/dịch vụ OA\r\n   * @param accessToken Access token của Official Account\r\n   * @param request Thông tin đơn hàng cần tạo\r\n   * @returns Thông tin đơn hàng đã tạo\r\n   */\r\n  public async createOrder(\r\n    accessToken: string,\r\n    request: CreateOrderRequest\r\n  ): Promise<OrderData> {\r\n    try {\r\n      // Validate request\r\n      this.validateCreateOrderRequest(request);\r\n\r\n      const result: ZaloResponse<OrderData> = await this.client.apiPost(\r\n        `${this.purchaseApiUrl}/create_order`,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to create order\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to create order: ${(error as Error).message}`,\r\n        PurchaseErrorCode.SYSTEM_ERROR,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Tạo đơn hàng với product_id\r\n   * @param accessToken Access token của Official Account\r\n   * @param beneficiary Đối tượng thụ hưởng (OA hoặc APP)\r\n   * @param productId ID sản phẩm\r\n   * @param voucherCode Mã giảm giá (tùy chọn)\r\n   * @returns Thông tin đơn hàng đã tạo\r\n   */\r\n  public async createOrderWithProduct(\r\n    accessToken: string,\r\n    beneficiary: \"OA\" | \"APP\",\r\n    productId: number,\r\n    voucherCode?: string\r\n  ): Promise<OrderData> {\r\n    const request = {\r\n      beneficiary,\r\n      product_id: productId,\r\n      ...(voucherCode && { voucher_code: voucherCode }),\r\n    };\r\n\r\n    return this.createOrder(accessToken, request);\r\n  }\r\n\r\n  /**\r\n   * Tạo đơn hàng với redeem_code (mã quà tặng)\r\n   * @param accessToken Access token của Official Account\r\n   * @param beneficiary Đối tượng thụ hưởng (OA hoặc APP)\r\n   * @param redeemCode Mã quà tặng\r\n   * @param voucherCode Mã giảm giá (tùy chọn)\r\n   * @returns Thông tin đơn hàng đã tạo\r\n   */\r\n  public async createOrderWithRedeemCode(\r\n    accessToken: string,\r\n    beneficiary: \"OA\" | \"APP\",\r\n    redeemCode: string,\r\n    voucherCode?: string\r\n  ): Promise<OrderData> {\r\n    const request = {\r\n      beneficiary,\r\n      redeem_code: redeemCode,\r\n      ...(voucherCode && { voucher_code: voucherCode }),\r\n    };\r\n\r\n    return this.createOrder(accessToken, request);\r\n  }\r\n\r\n  /**\r\n   * Validate create order request\r\n   * @param request Request to validate\r\n   */\r\n  private validateCreateOrderRequest(request: CreateOrderRequest): void {\r\n    // Validate beneficiary\r\n    if (!request.beneficiary || ![\"OA\", \"APP\"].includes(request.beneficiary)) {\r\n      throw new ZaloSDKError(\r\n        \"Beneficiary must be either 'OA' or 'APP'\",\r\n        PurchaseErrorCode.INVALID_BENEFICIARY\r\n      );\r\n    }\r\n\r\n    // Validate that either product_id or redeem_code is provided, but not both\r\n    const hasProductId = isProductOrderRequest(request);\r\n    const hasRedeemCode = isRedeemOrderRequest(request);\r\n\r\n    if (!hasProductId && !hasRedeemCode) {\r\n      throw new ZaloSDKError(\r\n        \"Either product_id or redeem_code must be provided\",\r\n        PurchaseErrorCode.INVALID_PRODUCT_ID\r\n      );\r\n    }\r\n\r\n    if (hasProductId && hasRedeemCode) {\r\n      throw new ZaloSDKError(\r\n        \"Cannot use both product_id and redeem_code in the same request\",\r\n        PurchaseErrorCode.INVALID_PRODUCT_ID\r\n      );\r\n    }\r\n\r\n    // Validate product_id\r\n    if (hasProductId && (!request.product_id || request.product_id <= 0)) {\r\n      throw new ZaloSDKError(\r\n        \"Product ID must be a positive number\",\r\n        PurchaseErrorCode.INVALID_PRODUCT_ID\r\n      );\r\n    }\r\n\r\n    // Validate redeem_code\r\n    if (hasRedeemCode && (!request.redeem_code || request.redeem_code.trim() === \"\")) {\r\n      throw new ZaloSDKError(\r\n        \"Redeem code cannot be empty\",\r\n        PurchaseErrorCode.INVALID_REDEEM_CODE\r\n      );\r\n    }\r\n\r\n    // Validate voucher_code if provided\r\n    if (request.voucher_code && request.voucher_code.trim() === \"\") {\r\n      throw new ZaloSDKError(\r\n        \"Voucher code cannot be empty if provided\",\r\n        PurchaseErrorCode.INVALID_VOUCHER_CODE\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Kiểm tra thời gian có thể tạo đơn hàng\r\n   * @returns true nếu trong thời gian cho phép tạo đơn hàng\r\n   */\r\n  public isOrderCreationTimeValid(): boolean {\r\n    const now = new Date();\r\n    const hours = now.getHours();\r\n    const minutes = now.getMinutes();\r\n\r\n    // Order chỉ được tạo từ 00:01 đến 23:54\r\n    if (hours === 0 && minutes === 0) {\r\n      return false; // 00:00\r\n    }\r\n    if (hours === 23 && minutes >= 55) {\r\n      return false; // 23:55 - 23:59\r\n    }\r\n\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * Tính toán thời gian hết hạn của OTT (One Time Token)\r\n   * @param createdTime Thời gian tạo đơn hàng (milliseconds)\r\n   * @returns Thời gian hết hạn OTT (milliseconds)\r\n   */\r\n  public calculateOTTExpiration(createdTime: number): number {\r\n    // OTT có hiệu lực trong vòng 5 phút\r\n    return createdTime + 5 * 60 * 1000;\r\n  }\r\n\r\n  /**\r\n   * Kiểm tra OTT còn hiệu lực hay không\r\n   * @param createdTime Thời gian tạo đơn hàng (milliseconds)\r\n   * @returns true nếu OTT còn hiệu lực\r\n   */\r\n  public isOTTValid(createdTime: number): boolean {\r\n    const now = Date.now();\r\n    const expirationTime = this.calculateOTTExpiration(createdTime);\r\n    return now < expirationTime;\r\n  }\r\n\r\n  /**\r\n   * Xác nhận thanh toán đơn hàng\r\n   * @param accessToken Access token của Official Account\r\n   * @param request Thông tin xác nhận đơn hàng\r\n   * @returns Thông tin đơn hàng đã được xác nhận thanh toán\r\n   */\r\n  public async confirmOrder(\r\n    accessToken: string,\r\n    request: ConfirmOrderRequest\r\n  ): Promise<ConfirmedOrderData> {\r\n    try {\r\n      // Validate request\r\n      this.validateConfirmOrderRequest(request);\r\n\r\n      const result: ZaloResponse<ConfirmedOrderData> = await this.client.apiPost(\r\n        `${this.purchaseApiUrl}/confirm_order`,\r\n        accessToken,\r\n        request\r\n      );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to confirm order\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to confirm order: ${(error as Error).message}`,\r\n        PurchaseErrorCode.SYSTEM_ERROR,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Xác nhận thanh toán đơn hàng với order ID và verified token\r\n   * @param accessToken Access token của Official Account\r\n   * @param orderId ID đơn hàng\r\n   * @param verifiedToken OTT token để xác nhận\r\n   * @returns Thông tin đơn hàng đã được xác nhận thanh toán\r\n   */\r\n  public async confirmOrderById(\r\n    accessToken: string,\r\n    orderId: string,\r\n    verifiedToken: string\r\n  ): Promise<ConfirmedOrderData> {\r\n    const request: ConfirmOrderRequest = {\r\n      order_id: orderId,\r\n      verified_token: verifiedToken,\r\n    };\r\n\r\n    return this.confirmOrder(accessToken, request);\r\n  }\r\n\r\n  /**\r\n   * Lấy thông tin sản phẩm theo ID\r\n   * @param productId ID sản phẩm\r\n   * @returns Thông tin sản phẩm hoặc undefined nếu không tìm thấy\r\n   */\r\n  public getProductInfo(productId: number): ProductInfo | undefined {\r\n    return PRODUCT_INFO[productId];\r\n  }\r\n\r\n  /**\r\n   * Lấy danh sách tất cả sản phẩm có sẵn\r\n   * @returns Danh sách thông tin sản phẩm\r\n   */\r\n  public getAllProducts(): ProductInfo[] {\r\n    return Object.values(PRODUCT_INFO);\r\n  }\r\n\r\n  /**\r\n   * Lấy danh sách sản phẩm theo loại\r\n   * @param category Loại sản phẩm\r\n   * @returns Danh sách sản phẩm thuộc loại đó\r\n   */\r\n  public getProductsByCategory(category: 'subscription' | 'gmf' | 'quota'): ProductInfo[] {\r\n    return Object.values(PRODUCT_INFO).filter(product => product.category === category);\r\n  }\r\n\r\n  /**\r\n   * Lấy danh sách sản phẩm theo đối tượng thụ hưởng\r\n   * @param beneficiary Đối tượng thụ hưởng\r\n   * @returns Danh sách sản phẩm phù hợp với đối tượng thụ hưởng\r\n   */\r\n  public getProductsByBeneficiary(beneficiary: \"OA\" | \"APP\"): ProductInfo[] {\r\n    return Object.values(PRODUCT_INFO).filter(product =>\r\n      product.beneficiary.includes(beneficiary)\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Kiểm tra sản phẩm có phù hợp với đối tượng thụ hưởng không\r\n   * @param productId ID sản phẩm\r\n   * @param beneficiary Đối tượng thụ hưởng\r\n   * @returns true nếu sản phẩm phù hợp với đối tượng thụ hưởng\r\n   */\r\n  public isProductCompatibleWithBeneficiary(\r\n    productId: number,\r\n    beneficiary: \"OA\" | \"APP\"\r\n  ): boolean {\r\n    const product = this.getProductInfo(productId);\r\n    if (!product) {\r\n      return false;\r\n    }\r\n    return product.beneficiary.includes(beneficiary);\r\n  }\r\n\r\n  // ==================== SPECIALIZED PRODUCT APIS ====================\r\n\r\n  /**\r\n   * Tạo đơn hàng gói OA Subscription\r\n   * @param accessToken Access token của Official Account\r\n   * @param subscriptionType Loại gói subscription\r\n   * @param voucherCode Mã giảm giá (tùy chọn)\r\n   * @returns Thông tin đơn hàng đã tạo\r\n   */\r\n  public async createOASubscriptionOrder(\r\n    accessToken: string,\r\n    subscriptionType: 'advanced_6m' | 'advanced_12m' | 'premium_6m' | 'premium_12m',\r\n    voucherCode?: string\r\n  ): Promise<OrderData> {\r\n    const productIdMap = {\r\n      'advanced_6m': OA_PRODUCTS.OA_ADVANCED_6M,\r\n      'advanced_12m': OA_PRODUCTS.OA_ADVANCED_12M,\r\n      'premium_6m': OA_PRODUCTS.OA_PREMIUM_6M,\r\n      'premium_12m': OA_PRODUCTS.OA_PREMIUM_12M,\r\n    };\r\n\r\n    const productId = productIdMap[subscriptionType];\r\n    return this.createOrderWithProduct(accessToken, \"OA\", productId, voucherCode);\r\n  }\r\n\r\n  /**\r\n   * Tạo đơn hàng gói GMF (Group Message Framework)\r\n   * @param accessToken Access token của Official Account\r\n   * @param memberLimit Giới hạn số thành viên\r\n   * @param voucherCode Mã giảm giá (tùy chọn)\r\n   * @returns Thông tin đơn hàng đã tạo\r\n   */\r\n  public async createGMFOrder(\r\n    accessToken: string,\r\n    memberLimit: 10 | 50 | 100 | 1000,\r\n    voucherCode?: string\r\n  ): Promise<OrderData> {\r\n    const productIdMap = {\r\n      10: OA_PRODUCTS.GMF_10_MEMBERS,\r\n      50: OA_PRODUCTS.GMF_50_MEMBERS,\r\n      100: OA_PRODUCTS.GMF_100_MEMBERS,\r\n      1000: OA_PRODUCTS.GMF_1000_MEMBERS,\r\n    };\r\n\r\n    const productId = productIdMap[memberLimit];\r\n    return this.createOrderWithProduct(accessToken, \"OA\", productId, voucherCode);\r\n  }\r\n\r\n  /**\r\n   * Tạo đơn hàng gói quota tin nhắn giao dịch\r\n   * @param accessToken Access token của Official Account\r\n   * @param beneficiary Đối tượng thụ hưởng (OA hoặc APP)\r\n   * @param quotaSize Kích thước gói quota\r\n   * @param voucherCode Mã giảm giá (tùy chọn)\r\n   * @returns Thông tin đơn hàng đã tạo\r\n   */\r\n  public async createTransactionQuotaOrder(\r\n    accessToken: string,\r\n    beneficiary: \"OA\" | \"APP\",\r\n    quotaSize: '5k' | '50k' | '500k',\r\n    voucherCode?: string\r\n  ): Promise<OrderData> {\r\n    const productIdMap = {\r\n      '5k': OA_PRODUCTS.TRANSACTION_5K,\r\n      '50k': OA_PRODUCTS.TRANSACTION_50K,\r\n      '500k': OA_PRODUCTS.TRANSACTION_500K,\r\n    };\r\n\r\n    const productId = productIdMap[quotaSize];\r\n\r\n    // Validate beneficiary compatibility\r\n    if (!this.isProductCompatibleWithBeneficiary(productId, beneficiary)) {\r\n      throw new ZaloSDKError(\r\n        `Product ${quotaSize} transaction quota is not compatible with beneficiary ${beneficiary}`,\r\n        PurchaseErrorCode.PRODUCT_NOT_AVAILABLE\r\n      );\r\n    }\r\n\r\n    return this.createOrderWithProduct(accessToken, beneficiary, productId, voucherCode);\r\n  }\r\n\r\n  // ==================== BULK ORDER OPERATIONS ====================\r\n\r\n  /**\r\n   * Tạo nhiều đơn hàng cùng lúc (batch operation)\r\n   * @param accessToken Access token của Official Account\r\n   * @param orders Danh sách đơn hàng cần tạo\r\n   * @returns Danh sách kết quả tạo đơn hàng\r\n   */\r\n  public async createMultipleOrders(\r\n    accessToken: string,\r\n    orders: CreateOrderRequest[]\r\n  ): Promise<Array<{ success: boolean; data?: OrderData; error?: string }>> {\r\n    const results: Array<{ success: boolean; data?: OrderData; error?: string }> = [];\r\n\r\n    for (const order of orders) {\r\n      try {\r\n        const orderData = await this.createOrder(accessToken, order);\r\n        results.push({ success: true, data: orderData });\r\n      } catch (error) {\r\n        results.push({\r\n          success: false,\r\n          error: error instanceof ZaloSDKError ? error.message : (error as Error).message\r\n        });\r\n      }\r\n    }\r\n\r\n    return results;\r\n  }\r\n\r\n  /**\r\n   * Tạo combo đơn hàng OA Premium + GMF\r\n   * @param accessToken Access token của Official Account\r\n   * @param premiumDuration Thời hạn gói Premium\r\n   * @param gmfMemberLimit Giới hạn thành viên GMF\r\n   * @param voucherCode Mã giảm giá (tùy chọn)\r\n   * @returns Danh sách kết quả tạo đơn hàng\r\n   */\r\n  public async createOAPremiumGMFCombo(\r\n    accessToken: string,\r\n    premiumDuration: '6m' | '12m',\r\n    gmfMemberLimit: 10 | 50 | 100 | 1000,\r\n    voucherCode?: string\r\n  ): Promise<{\r\n    premium: { success: boolean; data?: OrderData; error?: string };\r\n    gmf: { success: boolean; data?: OrderData; error?: string };\r\n  }> {\r\n    const premiumResult = await this.createOASubscriptionOrder(\r\n      accessToken,\r\n      premiumDuration === '6m' ? 'premium_6m' : 'premium_12m',\r\n      voucherCode\r\n    ).then(data => ({ success: true, data }))\r\n      .catch(error => ({\r\n        success: false,\r\n        error: error instanceof ZaloSDKError ? error.message : (error as Error).message\r\n      }));\r\n\r\n    const gmfResult = await this.createGMFOrder(\r\n      accessToken,\r\n      gmfMemberLimit,\r\n      voucherCode\r\n    ).then(data => ({ success: true, data }))\r\n      .catch(error => ({\r\n        success: false,\r\n        error: error instanceof ZaloSDKError ? error.message : (error as Error).message\r\n      }));\r\n\r\n    return {\r\n      premium: premiumResult,\r\n      gmf: gmfResult\r\n    };\r\n  }\r\n\r\n  // ==================== PRODUCT RECOMMENDATION ====================\r\n\r\n  /**\r\n   * Gợi ý sản phẩm dựa trên nhu cầu\r\n   * @param requirements Yêu cầu của khách hàng\r\n   * @returns Danh sách sản phẩm được gợi ý\r\n   */\r\n  public recommendProducts(requirements: {\r\n    beneficiary: \"OA\" | \"APP\";\r\n    budget?: 'low' | 'medium' | 'high';\r\n    features?: ('subscription' | 'group_messaging' | 'transaction_quota')[];\r\n    duration?: 'short' | 'long';\r\n  }): ProductInfo[] {\r\n    let recommendations: ProductInfo[] = [];\r\n\r\n    // Filter by beneficiary\r\n    const compatibleProducts = this.getProductsByBeneficiary(requirements.beneficiary);\r\n\r\n    // Add recommendations based on features\r\n    if (requirements.features?.includes('subscription')) {\r\n      const subscriptionProducts = compatibleProducts.filter(p => p.category === 'subscription');\r\n      if (requirements.duration === 'long') {\r\n        recommendations.push(...subscriptionProducts.filter(p => p.name.includes('12 tháng')));\r\n      } else {\r\n        recommendations.push(...subscriptionProducts.filter(p => p.name.includes('6 tháng')));\r\n      }\r\n    }\r\n\r\n    if (requirements.features?.includes('group_messaging')) {\r\n      const gmfProducts = compatibleProducts.filter(p => p.category === 'gmf');\r\n      if (requirements.budget === 'low') {\r\n        recommendations.push(...gmfProducts.filter(p => p.name.includes('10 thành viên')));\r\n      } else if (requirements.budget === 'high') {\r\n        recommendations.push(...gmfProducts.filter(p => p.name.includes('1000 thành viên')));\r\n      } else {\r\n        recommendations.push(...gmfProducts.filter(p =>\r\n          p.name.includes('50 thành viên') || p.name.includes('100 thành viên')\r\n        ));\r\n      }\r\n    }\r\n\r\n    if (requirements.features?.includes('transaction_quota')) {\r\n      const quotaProducts = compatibleProducts.filter(p => p.category === 'quota');\r\n      if (requirements.budget === 'low') {\r\n        recommendations.push(...quotaProducts.filter(p => p.name.includes('5k tin')));\r\n      } else if (requirements.budget === 'high') {\r\n        recommendations.push(...quotaProducts.filter(p => p.name.includes('500k tin')));\r\n      } else {\r\n        recommendations.push(...quotaProducts.filter(p => p.name.includes('50k tin')));\r\n      }\r\n    }\r\n\r\n    // Remove duplicates\r\n    return recommendations.filter((product, index, self) =>\r\n      index === self.findIndex(p => p.id === product.id)\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Validate confirm order request\r\n   * @param request Request to validate\r\n   */\r\n  private validateConfirmOrderRequest(request: ConfirmOrderRequest): void {\r\n    // Validate order_id\r\n    if (!request.order_id || request.order_id.trim() === \"\") {\r\n      throw new ZaloSDKError(\r\n        \"Order ID cannot be empty\",\r\n        PurchaseErrorCode.INVALID_ORDER_ID\r\n      );\r\n    }\r\n\r\n    // Validate verified_token\r\n    if (!request.verified_token || request.verified_token.trim() === \"\") {\r\n      throw new ZaloSDKError(\r\n        \"Verified token cannot be empty\",\r\n        PurchaseErrorCode.INVALID_VERIFIED_TOKEN\r\n      );\r\n    }\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\nimport {\r\n  AnonymousMessageRecipient,\r\n  AnonymousMessageResponse,\r\n} from \"../types/message\";\r\n\r\n/**\r\n * Attachment cho gửi ảnh ẩn danh\r\n */\r\nexport interface AnonymousImageAttachment {\r\n  type: \"template\";\r\n  payload: {\r\n    template_type: \"media\";\r\n    elements: Array<{\r\n      media_type: \"image\";\r\n      url?: string;\r\n      attachment_id?: string;\r\n    }>;\r\n  };\r\n}\r\n\r\n/**\r\n * Attachment cho gửi file ẩn danh\r\n */\r\nexport interface AnonymousFileAttachment {\r\n  type: \"file\";\r\n  payload: {\r\n    token: string;\r\n  };\r\n}\r\n\r\n/**\r\n * Attachment cho gửi sticker ẩn danh\r\n */\r\nexport interface AnonymousStickerAttachment {\r\n  type: \"template\";\r\n  payload: {\r\n    template_type: \"media\";\r\n    elements: Array<{\r\n      media_type: \"sticker\";\r\n      attachment_id: string;\r\n    }>;\r\n  };\r\n}\r\n\r\n/**\r\n * Request gửi tin nhắn văn bản đến người dùng ẩn danh\r\n */\r\nexport interface AnonymousTextMessageRequest {\r\n  recipient: AnonymousMessageRecipient;\r\n  message: {\r\n    text: string;\r\n  };\r\n}\r\n\r\n/**\r\n * Request gửi tin nhắn ảnh đến người dùng ẩn danh\r\n */\r\nexport interface AnonymousImageMessageRequest {\r\n  recipient: AnonymousMessageRecipient;\r\n  message: {\r\n    attachment: AnonymousImageAttachment;\r\n  };\r\n}\r\n\r\n/**\r\n * Request gửi tin nhắn file đến người dùng ẩn danh\r\n */\r\nexport interface AnonymousFileMessageRequest {\r\n  recipient: AnonymousMessageRecipient;\r\n  message: {\r\n    attachment: AnonymousFileAttachment;\r\n  };\r\n}\r\n\r\n/**\r\n * Request gửi tin nhắn sticker đến người dùng ẩn danh\r\n */\r\nexport interface AnonymousStickerMessageRequest {\r\n  recipient: AnonymousMessageRecipient;\r\n  message: {\r\n    attachment: AnonymousStickerAttachment;\r\n  };\r\n}\r\n\r\n/**\r\n * Service xử lý các API gửi tin nhắn đến người dùng ẩn danh của Zalo Official Account\r\n *\r\n * Bao gồm các chức năng:\r\n * - Gửi tin nhắn văn bản đến người dùng ẩn danh\r\n * - Gửi tin nhắn ảnh đến người dùng ẩn danh\r\n * - Gửi tin nhắn file đến người dùng ẩn danh\r\n * - Gửi tin nhắn sticker đến người dùng ẩn danh\r\n *\r\n * ĐIỀU KIỆN SỬ DỤNG:\r\n *\r\n * 1. QUYỀN TRUY CẬP:\r\n *    - Cần quyền: Gửi tin và thông báo qua OA\r\n *\r\n * 2. THÔNG TIN NGƯỜI DÙNG ẨN DANH:\r\n *    - anonymous_id: ID đại diện cho người dùng ẩn danh\r\n *    - conversation_id: ID của cuộc hội thoại\r\n *    - Các thông tin này nhận được từ webhook khi người dùng ẩn danh gửi tin nhắn\r\n *\r\n * 3. GIỚI HẠN:\r\n *    - Tin nhắn văn bản: Tối đa 2000 ký tự\r\n *    - Tin nhắn ảnh: Hỗ trợ định dạng JPG, PNG, dung lượng tối đa 1MB\r\n *    - Tin nhắn sticker: Sử dụng sticker_id từ nguồn https://stickers.zaloapp.com/\r\n *    - Tin nhắn file: Sử dụng token từ API upload file\r\n */\r\nexport class AnonymousMessageService {\r\n  private readonly endpoint = \"https://openapi.zalo.me/v2.0/oa/message\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Gửi tin nhắn văn bản đến người dùng ẩn danh\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận ẩn danh\r\n   * @param text Nội dung văn bản (tối đa 2000 ký tự)\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const result = await anonymousService.sendTextMessage(\r\n   *   accessToken,\r\n   *   { anonymous_id: 'xxx', conversation_id: 'yyy' },\r\n   *   'hello, world!'\r\n   * );\r\n   * ```\r\n   */\r\n  async sendTextMessage(\r\n    accessToken: string,\r\n    recipient: AnonymousMessageRecipient,\r\n    text: string\r\n  ): Promise<AnonymousMessageResponse> {\r\n    try {\r\n      // Validate text length theo docs: tối đa 2000 ký tự\r\n      if (text.length > 2000) {\r\n        throw new ZaloSDKError(\r\n          \"Nội dung văn bản không được vượt quá 2000 ký tự\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      if (text.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Nội dung văn bản không được để trống\", -1);\r\n      }\r\n\r\n      const request: AnonymousTextMessageRequest = {\r\n        recipient,\r\n        message: { text },\r\n      };\r\n\r\n      const result: ZaloResponse<AnonymousMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send anonymous text message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send anonymous text message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn ảnh đến người dùng ẩn danh\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận ẩn danh\r\n   * @param imageUrl URL của ảnh (định dạng JPG/PNG, tối đa 1MB) HOẶC attachment_id từ upload\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * // Gửi bằng URL\r\n   * const result = await anonymousService.sendImageMessage(\r\n   *   accessToken,\r\n   *   { anonymous_id: 'xxx', conversation_id: 'yyy' },\r\n   *   'https://example.com/image.jpg'\r\n   * );\r\n   *\r\n   * // Gửi bằng attachment_id\r\n   * const result2 = await anonymousService.sendImageMessage(\r\n   *   accessToken,\r\n   *   { anonymous_id: 'xxx', conversation_id: 'yyy' },\r\n   *   undefined,\r\n   *   'uploaded_attachment_id'\r\n   * );\r\n   * ```\r\n   */\r\n  async sendImageMessage(\r\n    accessToken: string,\r\n    recipient: AnonymousMessageRecipient,\r\n    imageUrl?: string,\r\n    attachmentId?: string\r\n  ): Promise<AnonymousMessageResponse> {\r\n    try {\r\n      if (!imageUrl && !attachmentId) {\r\n        throw new ZaloSDKError(\r\n          \"Phải cung cấp imageUrl hoặc attachmentId\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      const attachment: AnonymousImageAttachment = {\r\n        type: \"template\",\r\n        payload: {\r\n          template_type: \"media\",\r\n          elements: [\r\n            {\r\n              media_type: \"image\",\r\n              ...(imageUrl && { url: imageUrl }),\r\n              ...(attachmentId && { attachment_id: attachmentId }),\r\n            },\r\n          ],\r\n        },\r\n      };\r\n\r\n      const request: AnonymousImageMessageRequest = {\r\n        recipient,\r\n        message: { attachment },\r\n      };\r\n\r\n      const result: ZaloResponse<AnonymousMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send anonymous image message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send anonymous image message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn file đến người dùng ẩn danh\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận ẩn danh\r\n   * @param fileToken Token từ API upload file\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * // Đầu tiên upload file\r\n   * const uploadResult = await messageService.uploadFile(accessToken, fileData, fileName);\r\n   *\r\n   * // Sau đó gửi tin nhắn file\r\n   * const result = await anonymousService.sendFileMessage(\r\n   *   accessToken,\r\n   *   { anonymous_id: 'xxx', conversation_id: 'yyy' },\r\n   *   uploadResult.token\r\n   * );\r\n   * ```\r\n   */\r\n  async sendFileMessage(\r\n    accessToken: string,\r\n    recipient: AnonymousMessageRecipient,\r\n    fileToken: string\r\n  ): Promise<AnonymousMessageResponse> {\r\n    try {\r\n      if (!fileToken || fileToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\"File token không được để trống\", -1);\r\n      }\r\n\r\n      const attachment: AnonymousFileAttachment = {\r\n        type: \"file\",\r\n        payload: {\r\n          token: fileToken,\r\n        },\r\n      };\r\n\r\n      const request: AnonymousFileMessageRequest = {\r\n        recipient,\r\n        message: { attachment },\r\n      };\r\n\r\n      const result: ZaloResponse<AnonymousMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send anonymous file message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send anonymous file message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn sticker đến người dùng ẩn danh\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param recipient Thông tin người nhận ẩn danh\r\n   * @param stickerId ID của sticker (lấy từ https://stickers.zaloapp.com/)\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const result = await anonymousService.sendStickerMessage(\r\n   *   accessToken,\r\n   *   { anonymous_id: 'xxx', conversation_id: 'yyy' },\r\n   *   'sticker_id_from_zalo'\r\n   * );\r\n   * ```\r\n   */\r\n  async sendStickerMessage(\r\n    accessToken: string,\r\n    recipient: AnonymousMessageRecipient,\r\n    stickerId: string\r\n  ): Promise<AnonymousMessageResponse> {\r\n    try {\r\n      if (!stickerId || stickerId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Sticker ID không được để trống\", -1);\r\n      }\r\n\r\n      const attachment: AnonymousStickerAttachment = {\r\n        type: \"template\",\r\n        payload: {\r\n          template_type: \"media\",\r\n          elements: [\r\n            {\r\n              media_type: \"sticker\",\r\n              attachment_id: stickerId,\r\n            },\r\n          ],\r\n        },\r\n      };\r\n\r\n      const request: AnonymousStickerMessageRequest = {\r\n        recipient,\r\n        message: { attachment },\r\n      };\r\n\r\n      const result: ZaloResponse<AnonymousMessageResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send anonymous sticker message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send anonymous sticker message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\nimport {\r\n  MessageRecipient,\r\n  SenderAction,\r\n  MessageReactionResponse,\r\n  ReactIcon,\r\n} from \"../types/message\";\r\n\r\n/**\r\n * Request thả biểu tượng cảm xúc vào tin nhắn\r\n */\r\nexport interface MessageReactionRequest {\r\n  recipient: MessageRecipient;\r\n  sender_action: SenderAction;\r\n}\r\n\r\n/**\r\n * Service xử lý API thả biểu tượng cảm xúc vào tin nhắn của Zalo Official Account\r\n *\r\n * Bao gồm chức năng:\r\n * - Thả biểu tượng cảm xúc vào tin nhắn\r\n * - Thu hồi biểu tượng cảm xúc\r\n *\r\n * ĐIỀU KIỆN SỬ DỤNG:\r\n *\r\n * 1. QUYỀN TRUY CẬP:\r\n *    - Cần quyền: Gửi tin nhắn / Thả biểu tượng cảm xúc vào tin nhắn\r\n *\r\n * 2. GIỚI HẠN:\r\n *    - Một message_id chỉ được thả tối đa 50 biểu tượng cảm xúc\r\n *    - API này KHÔNG bị tính vào quota chủ động hàng tháng\r\n *\r\n * 3. CÁC BIỂU TƯỢNG CẢM XÚC HỖ TRỢ:\r\n *    - :> (Thích - Like)\r\n *    - --b (Haha)\r\n *    - :-(( (Buồn - Sad)\r\n *    - /-strong (Mạnh mẽ - Strong)\r\n *    - /-heart (Yêu thích - Love)\r\n *    - :-h (Ngạc nhiên - Wow)\r\n *    - :o (Wow)\r\n *    - /-remove (Thu hồi biểu tượng cảm xúc)\r\n */\r\nexport class MessageReactionService {\r\n  private readonly endpoint = \"https://openapi.zalo.me/v2.0/oa/message\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Thả biểu tượng cảm xúc vào tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thả cảm xúc\r\n   * @param reactIcon Biểu tượng cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * // Thả icon tim\r\n   * const result = await reactionService.reactToMessage(\r\n   *   accessToken,\r\n   *   '186729651760683225',\r\n   *   '789f573aecf8a9a4f0eb',\r\n   *   '/-heart'\r\n   * );\r\n   *\r\n   * // Thu hồi biểu tượng cảm xúc\r\n   * const removeResult = await reactionService.reactToMessage(\r\n   *   accessToken,\r\n   *   '186729651760683225',\r\n   *   '789f573aecf8a9a4f0eb',\r\n   *   '/-remove'\r\n   * );\r\n   * ```\r\n   */\r\n  async reactToMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string,\r\n    reactIcon: ReactIcon\r\n  ): Promise<MessageReactionResponse> {\r\n    try {\r\n      // Validate inputs\r\n      if (!userId || userId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"User ID không được để trống\", -1);\r\n      }\r\n\r\n      if (!messageId || messageId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Message ID không được để trống\", -1);\r\n      }\r\n\r\n      // Danh sách các react icon hợp lệ\r\n      const validIcons: ReactIcon[] = [\r\n        \":>\",\r\n        \"--b\",\r\n        \":-((\",\r\n        \"/-strong\",\r\n        \"/-heart\",\r\n        \":-h\",\r\n        \":o\",\r\n        \"/-remove\",\r\n      ];\r\n\r\n      if (!validIcons.includes(reactIcon)) {\r\n        throw new ZaloSDKError(\r\n          `Biểu tượng cảm xúc không hợp lệ. Các giá trị hợp lệ: ${validIcons.join(\r\n            \", \"\r\n          )}`,\r\n          -1\r\n        );\r\n      }\r\n\r\n      const request: MessageReactionRequest = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        sender_action: {\r\n          react_icon: reactIcon,\r\n          react_message_id: messageId,\r\n        },\r\n      };\r\n\r\n      const result: ZaloResponse<MessageReactionResponse> =\r\n        await this.client.apiPost(this.endpoint, accessToken, request);\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to react to message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to react to message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Thả icon \"Thích\" (Like) vào tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thả cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   */\r\n  async likeMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string\r\n  ): Promise<MessageReactionResponse> {\r\n    return this.reactToMessage(accessToken, userId, messageId, \":>\");\r\n  }\r\n\r\n  /**\r\n   * Thả icon \"Haha\" vào tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thả cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   */\r\n  async hahaMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string\r\n  ): Promise<MessageReactionResponse> {\r\n    return this.reactToMessage(accessToken, userId, messageId, \"--b\");\r\n  }\r\n\r\n  /**\r\n   * Thả icon \"Buồn\" (Sad) vào tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thả cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   */\r\n  async sadMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string\r\n  ): Promise<MessageReactionResponse> {\r\n    return this.reactToMessage(accessToken, userId, messageId, \":-((\");\r\n  }\r\n\r\n  /**\r\n   * Thả icon \"Mạnh mẽ\" (Strong) vào tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thả cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   */\r\n  async strongMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string\r\n  ): Promise<MessageReactionResponse> {\r\n    return this.reactToMessage(accessToken, userId, messageId, \"/-strong\");\r\n  }\r\n\r\n  /**\r\n   * Thả icon \"Yêu thích\" (Love) vào tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thả cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   */\r\n  async loveMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string\r\n  ): Promise<MessageReactionResponse> {\r\n    return this.reactToMessage(accessToken, userId, messageId, \"/-heart\");\r\n  }\r\n\r\n  /**\r\n   * Thả icon \"Ngạc nhiên\" (Wow) vào tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thả cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   */\r\n  async wowMessage(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string\r\n  ): Promise<MessageReactionResponse> {\r\n    return this.reactToMessage(accessToken, userId, messageId, \":-h\");\r\n  }\r\n\r\n  /**\r\n   * Thu hồi biểu tượng cảm xúc khỏi tin nhắn\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param userId ID của người nhận\r\n   * @param messageId message_id của tin nhắn cần thu hồi cảm xúc\r\n   * @returns Thông tin tin nhắn phản hồi\r\n   */\r\n  async removeReaction(\r\n    accessToken: string,\r\n    userId: string,\r\n    messageId: string\r\n  ): Promise<MessageReactionResponse> {\r\n    return this.reactToMessage(accessToken, userId, messageId, \"/-remove\");\r\n  }\r\n}\r\n\r\n/**\r\n * Enum các loại biểu tượng cảm xúc hỗ trợ\r\n */\r\nexport enum ReactionIcon {\r\n  /** Thích (Like) */\r\n  LIKE = \":>\",\r\n  /** Haha */\r\n  HAHA = \"--b\",\r\n  /** Buồn (Sad) */\r\n  SAD = \":-((\",\r\n  /** Mạnh mẽ (Strong) */\r\n  STRONG = \"/-strong\",\r\n  /** Yêu thích (Love) */\r\n  HEART = \"/-heart\",\r\n  /** Ngạc nhiên (Wow) */\r\n  WOW_H = \":-h\",\r\n  /** Wow */\r\n  WOW_O = \":o\",\r\n  /** Thu hồi biểu tượng cảm xúc */\r\n  REMOVE = \"/-remove\",\r\n}\r\n","import { ZaloClient } from \"../clients/zalo-client\";\r\nimport { ZaloResponse, ZaloSDKError } from \"../types/common\";\r\nimport {\r\n  MessageRecipient,\r\n  MiniAppMessageRequest,\r\n  MiniAppMessageResponse,\r\n} from \"../types/message\";\r\n\r\n/**\r\n * Request gửi tin nhắn miniapp\r\n */\r\nexport interface MiniAppSendMessageRequest {\r\n  recipient: MessageRecipient;\r\n  message: MiniAppMessageRequest;\r\n}\r\n\r\n/**\r\n * Service xử lý API gửi tin nhắn miniapp của Zalo Official Account\r\n *\r\n * Bao gồm chức năng:\r\n * - Gửi tin nhắn miniapp với template tùy chỉnh\r\n *\r\n * ĐIỀU KIỆN SỬ DỤNG:\r\n *\r\n * 1. QUYỀN TRUY CẬP:\r\n *    - Cần quyền: Gửi tin và thông báo qua OA\r\n *\r\n * 2. THÔNG TIN BẮT BUỘC:\r\n *    - access_token: Token của OA\r\n *    - miniapp_message_token: Token được sinh ra từ miniapp, đại diện cho miniapp\r\n *    - user_id: Phải khớp với user đã tương tác với Miniapp\r\n *    - template_type: Token của template miniapp\r\n *    - template_data: Các thuộc tính của mẫu tin (được quy định theo từng template_type)\r\n *\r\n * 3. QUOTA (HẠN MỨC):\r\n *    API này có thể trả về thông tin quota trong response:\r\n *    - reply: Tin Tư vấn miễn phí trong hạn mức 8 tin 48 giờ\r\n *    - sub_quota: Tin Tư vấn miễn phí theo gói dịch vụ OA\r\n *    - purchase_quota: Tin trong hạn mức gói tính năng lẻ\r\n *    - reward_quota: Tin trong hạn mức Redeem code\r\n *\r\n *    Các loại quota không có thông tin chi tiết:\r\n *    - Không có quota info: Tin không tính quota (ví dụ: tin trong 48h không còn hạn mức)\r\n */\r\nexport class MiniAppMessageService {\r\n  private readonly endpoint = \"https://openapi.zalo.me/v3.0/oa/message/cs/miniapp\";\r\n\r\n  constructor(private readonly client: ZaloClient) {}\r\n\r\n  /**\r\n   * Gửi tin nhắn miniapp đến người dùng\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param miniappMessageToken Token được sinh ra từ miniapp\r\n   * @param userId ID của người nhận (phải khớp với user đã tương tác với Miniapp)\r\n   * @param templateType Token của template miniapp\r\n   * @param templateData Các thuộc tính của mẫu tin (tuỳ theo từng template_type)\r\n   * @returns Thông tin tin nhắn đã gửi (bao gồm quota info nếu có)\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const result = await miniappService.sendMessage(\r\n   *   accessToken,\r\n   *   'miniapp_message_token',\r\n   *   '4153814862349165539',\r\n   *   'FB0001',\r\n   *   {\r\n   *     customer_name: 'Tùng Nguyễn',\r\n   *     queue_number: '12',\r\n   *     note: 'Khách hàng thanh toán bằng thẻ',\r\n   *     buttons: [\r\n   *       {\r\n   *         title: 'Chi tiết đơn hàng',\r\n   *         image_icon: 'https://stc-zmp.zadn.vn/oa/basket.png',\r\n   *         url: 'https://zalo.me/s/194839900003483517/'\r\n   *       },\r\n   *       {\r\n   *         title: 'Đánh giá',\r\n   *         image_icon: 'https://stc-zmp.zadn.vn/oa/star.png',\r\n   *         url: 'https://zalo.me/s/194839900003483517/'\r\n   *       }\r\n   *     ]\r\n   *   }\r\n   * );\r\n   *\r\n   * // Kiểm tra quota\r\n   * if (result.quota) {\r\n   *   console.log('Quota type:', result.quota.quota_type);\r\n   *   console.log('Remaining:', result.quota.remain);\r\n   * }\r\n   * ```\r\n   */\r\n  async sendMessage(\r\n    accessToken: string,\r\n    miniappMessageToken: string,\r\n    userId: string,\r\n    templateType: string,\r\n    templateData: Record<string, any>\r\n  ): Promise<MiniAppMessageResponse> {\r\n    try {\r\n      // Validate inputs\r\n      if (!miniappMessageToken || miniappMessageToken.trim().length === 0) {\r\n        throw new ZaloSDKError(\r\n          \"Miniapp message token không được để trống\",\r\n          -1\r\n        );\r\n      }\r\n\r\n      if (!userId || userId.trim().length === 0) {\r\n        throw new ZaloSDKError(\"User ID không được để trống\", -1);\r\n      }\r\n\r\n      if (!templateType || templateType.trim().length === 0) {\r\n        throw new ZaloSDKError(\"Template type không được để trống\", -1);\r\n      }\r\n\r\n      if (!templateData || Object.keys(templateData).length === 0) {\r\n        throw new ZaloSDKError(\"Template data không được để trống\", -1);\r\n      }\r\n\r\n      const request: MiniAppSendMessageRequest = {\r\n        recipient: {\r\n          user_id: userId,\r\n        },\r\n        message: {\r\n          template_type: templateType,\r\n          template_data: templateData,\r\n        },\r\n      };\r\n\r\n      // Gọi API với custom header miniapp_message_token\r\n      const result: ZaloResponse<MiniAppMessageResponse> =\r\n        await this.client.apiPostWithHeaders(\r\n          this.endpoint,\r\n          accessToken,\r\n          request,\r\n          {\r\n            miniapp_messsage_token: miniappMessageToken,\r\n          }\r\n        );\r\n\r\n      if (result.error !== 0) {\r\n        throw new ZaloSDKError(\r\n          result.message || \"Failed to send miniapp message\",\r\n          result.error,\r\n          result\r\n        );\r\n      }\r\n\r\n      if (!result.data) {\r\n        throw new ZaloSDKError(\"No response data received\", -1);\r\n      }\r\n\r\n      return result.data;\r\n    } catch (error) {\r\n      if (error instanceof ZaloSDKError) {\r\n        throw error;\r\n      }\r\n      throw new ZaloSDKError(\r\n        `Failed to send miniapp message: ${(error as Error).message}`,\r\n        -1,\r\n        error\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gửi tin nhắn miniapp với template queue notification\r\n   * (Template thông báo số thứ tự trong hàng đợi)\r\n   *\r\n   * @param accessToken Access token của Official Account\r\n   * @param miniappMessageToken Token từ miniapp\r\n   * @param userId ID của người nhận\r\n   * @param customerName Tên khách hàng\r\n   * @param queueNumber Số thứ tự trong hàng đợi\r\n   * @param note Ghi chú thêm\r\n   * @param buttons Danh sách các button kèm theo\r\n   * @returns Thông tin tin nhắn đã gửi\r\n   */\r\n  async sendQueueNotification(\r\n    accessToken: string,\r\n    miniappMessageToken: string,\r\n    userId: string,\r\n    customerName: string,\r\n    queueNumber: string,\r\n    note: string,\r\n    buttons?: Array<{\r\n      title: string;\r\n      image_icon: string;\r\n      url: string;\r\n    }>\r\n  ): Promise<MiniAppMessageResponse> {\r\n    const templateData: Record<string, any> = {\r\n      customer_name: customerName,\r\n      queue_number: queueNumber,\r\n      note: note,\r\n    };\r\n\r\n    if (buttons && buttons.length > 0) {\r\n      templateData.buttons = buttons;\r\n    }\r\n\r\n    return this.sendMessage(\r\n      accessToken,\r\n      miniappMessageToken,\r\n      userId,\r\n      \"FB0001\",\r\n      templateData\r\n    );\r\n  }\r\n}\r\n\r\n/**\r\n * Enum các loại quota (hạn mức) khi gửi tin nhắn miniapp\r\n */\r\nexport enum MiniAppQuotaType {\r\n  /** Tin Tư vấn miễn phí trong hạn mức 8 tin 48 giờ */\r\n  REPLY = \"reply\",\r\n  /** Tin Tư vấn miễn phí theo gói dịch vụ OA */\r\n  SUB_QUOTA = \"sub_quota\",\r\n  /** Tin trong hạn mức gói tính năng lẻ */\r\n  PURCHASE_QUOTA = \"purchase_quota\",\r\n  /** Tin trong hạn mức Redeem code */\r\n  REWARD_QUOTA = \"reward_quota\",\r\n}\r\n\r\n/**\r\n * Enum các loại owner sở hữu Quota Package\r\n */\r\nexport enum QuotaOwnerType {\r\n  /** Thực thể sở hữu là OA */\r\n  OA = \"OA\",\r\n  /** Thực thể sở hữu là App */\r\n  APP = \"App\",\r\n}\r\n","/**\r\n * Main Zalo SDK class\r\n */\r\n\r\nimport { ZaloClient } from \"./clients/zalo-client\";\r\nimport { AuthService } from \"./services/auth.service\";\r\nimport { OAService } from \"./services/oa.service\";\r\n// import { MessageService } from \"./services/message.service\"; // Replaced by specialized services\r\nimport { UserService } from \"./services/user.service\";\r\n// import { WebhookService } from \"./services/webhook.service\";\r\nimport { ZNSService } from \"./services/zns.service\";\r\nimport { GroupMessageService } from \"./services/group-message.service\";\r\nimport { GroupManagementService } from \"./services/group-management.service\";\r\nimport { ArticleService } from \"./services/article.service\";\r\nimport { VideoUploadService } from \"./services/video-upload.service\";\r\n\r\n// Message Services\r\nimport { ConsultationService } from \"./services/consultation.service\";\r\nimport { TransactionService } from \"./services/transaction.service\";\r\nimport { PromotionService } from \"./services/promotion.service\";\r\nimport { GeneralMessageService } from \"./services/general-message.service\";\r\nimport { MessageManagementService } from \"./services/message-management.service\";\r\nimport { BroadcastService } from \"./services/broadcast.service\";\r\nimport { PurchaseService } from \"./services/purchase.service\";\r\n// New services from api.md\r\nimport { AnonymousMessageService } from \"./services/anonymous-message.service\";\r\nimport { MessageReactionService } from \"./services/message-reaction.service\";\r\nimport { MiniAppMessageService } from \"./services/miniapp-message.service\";\r\nimport {\r\n  ZaloSDKConfig,\r\n  ZaloSDKError,\r\n  Logger,\r\n  ConsoleLogger,\r\n} from \"./types/common\";\r\nimport {\r\n  AccessToken,\r\n  AuthCodeParams,\r\n  RefreshTokenParams,\r\n  SocialUserInfo,\r\n} from \"./types/auth\";\r\nimport { OAInfo, MessageQuota } from \"./types/oa\";\r\nimport { SendMessageResponse } from \"./types/message\";\r\nimport { UserInfo, UserListRequest, UserListResponse } from \"./types/user\";\r\n// import { WebhookHandlers } from \"./types/webhook\";\r\n\r\n/**\r\n * Main Zalo SDK class providing access to all Zalo APIs\r\n */\r\nexport class ZaloSDK {\r\n  private readonly client: ZaloClient;\r\n  private readonly logger: Logger;\r\n\r\n  // Services\r\n  public readonly auth: AuthService;\r\n  public readonly oa: OAService;\r\n  // public readonly message: MessageService; // Replaced by specialized message services\r\n  public readonly user: UserService;\r\n  // public readonly webhook: WebhookService;\r\n  public readonly zns: ZNSService;\r\n  public readonly groupMessage: GroupMessageService;\r\n  public readonly groupManagement: GroupManagementService;\r\n  public readonly article: ArticleService;\r\n  public readonly videoUpload: VideoUploadService;\r\n\r\n  // Message Services - Specialized by message type\r\n  public readonly consultation: ConsultationService;\r\n  public readonly transaction: TransactionService;\r\n  public readonly promotion: PromotionService;\r\n  public readonly generalMessage: GeneralMessageService;\r\n  public readonly messageManagement: MessageManagementService;\r\n  public readonly broadcast: BroadcastService;\r\n  public readonly purchase: PurchaseService;\r\n\r\n  // New services from api.md\r\n  public readonly anonymousMessage: AnonymousMessageService;\r\n  public readonly messageReaction: MessageReactionService;\r\n  public readonly miniappMessage: MiniAppMessageService;\r\n\r\n  // Configuration\r\n  public readonly config: Required<ZaloSDKConfig>;\r\n\r\n  constructor(config: ZaloSDKConfig) {\r\n    // Validate required configuration\r\n    if (!config.appId) {\r\n      throw new ZaloSDKError(\"appId is required in SDK configuration\");\r\n    }\r\n    if (!config.appSecret) {\r\n      throw new ZaloSDKError(\"appSecret is required in SDK configuration\");\r\n    }\r\n\r\n    // Set default configuration\r\n    this.config = {\r\n      appId: config.appId,\r\n      appSecret: config.appSecret,\r\n      timeout: config.timeout || 30000,\r\n      debug: config.debug || false,\r\n      apiBaseUrl: config.apiBaseUrl || \"https://openapi.zalo.me\",\r\n      retry: {\r\n        attempts: config.retry?.attempts || 3,\r\n        delay: config.retry?.delay || 1000,\r\n        ...config.retry,\r\n      },\r\n    };\r\n\r\n    this.logger = new ConsoleLogger(this.config.debug);\r\n    this.client = new ZaloClient(this.config);\r\n\r\n    // Initialize services\r\n    this.auth = new AuthService(\r\n      this.client,\r\n      this.config.appId,\r\n      this.config.appSecret\r\n    );\r\n    this.oa = new OAService(this.client);\r\n    // this.message = new MessageService(this.client); // Replaced by specialized message services\r\n    this.user = new UserService(this.client);\r\n    // this.webhook = new WebhookService(this.config.appSecret, this.config.debug);\r\n    this.zns = new ZNSService(this.client);\r\n    this.groupMessage = new GroupMessageService(this.client);\r\n    this.groupManagement = new GroupManagementService(this.client);\r\n    this.article = new ArticleService(this.client);\r\n    this.videoUpload = new VideoUploadService(this.client);\r\n\r\n    // Initialize message services\r\n    this.consultation = new ConsultationService(this.client);\r\n    this.transaction = new TransactionService(this.client);\r\n    this.promotion = new PromotionService(this.client);\r\n    this.generalMessage = new GeneralMessageService(this.client);\r\n    this.messageManagement = new MessageManagementService(this.client);\r\n    this.broadcast = new BroadcastService(this.client);\r\n    this.purchase = new PurchaseService(this.client);\r\n\r\n    // Initialize new services from api.md\r\n    this.anonymousMessage = new AnonymousMessageService(this.client);\r\n    this.messageReaction = new MessageReactionService(this.client);\r\n    this.miniappMessage = new MiniAppMessageService(this.client);\r\n\r\n    this.logger.info(\"Zalo SDK initialized\", {\r\n      appId: this.config.appId,\r\n      debug: this.config.debug,\r\n      timeout: this.config.timeout,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Quick method to get OA access token from authorization code\r\n   */\r\n  public async getOAAccessToken(\r\n    code: string,\r\n    redirectUri: string\r\n  ): Promise<AccessToken> {\r\n    const params: AuthCodeParams = {\r\n      app_id: this.config.appId,\r\n      app_secret: this.config.appSecret,\r\n      code,\r\n      redirect_uri: redirectUri,\r\n    };\r\n\r\n    return this.auth.getOAAccessToken(params);\r\n  }\r\n\r\n  /**\r\n   * Quick method to get Social access token from authorization code\r\n   */\r\n  public async getSocialAccessToken(\r\n    code: string,\r\n    redirectUri: string,\r\n    codeVerifier?: string\r\n  ): Promise<AccessToken> {\r\n    const params: AuthCodeParams = {\r\n      app_id: this.config.appId,\r\n      app_secret: this.config.appSecret,\r\n      code,\r\n      redirect_uri: redirectUri,\r\n      code_verifier: codeVerifier || \"\",\r\n    };\r\n\r\n    return this.auth.getSocialAccessToken(params);\r\n  }\r\n\r\n  /**\r\n   * Quick method to refresh OA access token\r\n   */\r\n  public async refreshOAAccessToken(\r\n    refreshToken: string\r\n  ): Promise<AccessToken> {\r\n    const params: RefreshTokenParams = {\r\n      app_id: this.config.appId,\r\n      app_secret: this.config.appSecret,\r\n      refresh_token: refreshToken,\r\n    };\r\n\r\n    return this.auth.refreshOAAccessToken(params);\r\n  }\r\n\r\n  /**\r\n   * Quick method to refresh Social access token\r\n   */\r\n  public async refreshSocialAccessToken(\r\n    refreshToken: string\r\n  ): Promise<AccessToken> {\r\n    const params: RefreshTokenParams = {\r\n      app_id: this.config.appId,\r\n      app_secret: this.config.appSecret,\r\n      refresh_token: refreshToken,\r\n    };\r\n\r\n    return this.auth.refreshSocialAccessToken(params);\r\n  }\r\n\r\n  /**\r\n   * Quick method to get OA information\r\n   */\r\n  public async getOAInfo(accessToken: string): Promise<OAInfo> {\r\n    return this.oa.getOAInfo(accessToken);\r\n  }\r\n\r\n  /**\r\n   * Quick method to get message quota\r\n   */\r\n  public async getMessageQuota(accessToken: string): Promise<MessageQuota> {\r\n    return this.oa.getMessageQuota(accessToken);\r\n  }\r\n\r\n  /**\r\n   * Quick method to get Social user information\r\n   */\r\n  public async getSocialUserInfo(\r\n    accessToken: string,\r\n    fields?: string\r\n  ): Promise<SocialUserInfo> {\r\n    return this.auth.getSocialUserInfo(accessToken, fields);\r\n  }\r\n\r\n  /**\r\n   * Quick method to send consultation text message\r\n   */\r\n  public async sendConsultationText(\r\n    accessToken: string,\r\n    userId: string,\r\n    text: string\r\n  ): Promise<SendMessageResponse> {\r\n    return this.consultation.sendTextMessage(\r\n      accessToken,\r\n      { user_id: userId },\r\n      { type: \"text\", text }\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Quick method to get user information\r\n   */\r\n  public async getUserInfo(\r\n    accessToken: string,\r\n    userId: string\r\n  ): Promise<UserInfo> {\r\n    return this.user.getUserInfo(accessToken, userId);\r\n  }\r\n\r\n  /**\r\n   * Quick method to get user list\r\n   */\r\n  public async getUserList(\r\n    accessToken: string,\r\n    request: UserListRequest\r\n  ): Promise<UserListResponse> {\r\n    return this.user.getUserList(accessToken, request);\r\n  }\r\n\r\n  /**\r\n   * Quick method to get all followers\r\n   */\r\n  public async getFollowers(accessToken: string): Promise<UserListResponse> {\r\n    return this.user.getFollowers(accessToken);\r\n  }\r\n\r\n  /**\r\n   * Quick method to register webhook handlers\r\n   */\r\n  // public registerWebhookHandlers(handlers: WebhookHandlers): void {\r\n  //   this.webhook.registerHandlers(handlers);\r\n  // }\r\n\r\n  /**\r\n   * Quick method to process webhook\r\n   */\r\n  // public async processWebhook(\r\n  //   payload: string,\r\n  //   signature?: string,\r\n  //   timestamp?: string\r\n  // ): Promise<void> {\r\n  //   return this.webhook.processWebhook(payload, signature, timestamp);\r\n  // }\r\n\r\n  /**\r\n   * Create OA authorization URL with PKCE support\r\n   * @deprecated Use auth.createOAAuthUrl() directly for full control over PKCE and state\r\n   */\r\n  public createOAAuthUrl(redirectUri: string, state?: string): string {\r\n    const result = this.auth.createOAAuthUrl(redirectUri, state, undefined, false);\r\n    return result.url;\r\n  }\r\n\r\n  /**\r\n   * Create OA authorization URL with full PKCE support\r\n   * Returns both URL and state for enhanced security\r\n   */\r\n  public createSecureOAAuthUrl(redirectUri: string, state?: string, enablePKCE: boolean = true) {\r\n    const result = this.auth.createOAAuthUrl(redirectUri, state, undefined, enablePKCE);\r\n    return result;\r\n  }\r\n\r\n  /**\r\n   * Create Social authorization URL\r\n   */\r\n  public createSocialAuthUrl(redirectUri: string, state?: string): string {\r\n    return this.auth.createSocialAuthUrl(redirectUri, state);\r\n  }\r\n\r\n  /**\r\n   * Generate PKCE for Social API\r\n   */\r\n  public generatePKCE() {\r\n    return this.auth.generatePKCE();\r\n  }\r\n\r\n  /**\r\n   * Validate access token\r\n   */\r\n  public async validateAccessToken(\r\n    accessToken: string,\r\n    scope: \"oa\" | \"social\" = \"social\"\r\n  ): Promise<boolean> {\r\n    try {\r\n      if (scope === \"oa\") {\r\n        return this.oa.validateOAToken(accessToken);\r\n      } else {\r\n        const validation = await this.auth.validateAccessToken(accessToken);\r\n        return validation.valid;\r\n      }\r\n    } catch (error) {\r\n      this.logger.warn(`Token validation failed: ${(error as Error).message}`);\r\n      return false;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get SDK version\r\n   */\r\n  public getVersion(): string {\r\n    return \"1.0.0\";\r\n  }\r\n\r\n  /**\r\n   * Get SDK configuration (without sensitive data)\r\n   */\r\n  public getConfig(): Omit<Required<ZaloSDKConfig>, \"appSecret\"> {\r\n    const { appSecret, ...safeConfig } = this.config;\r\n    return safeConfig;\r\n  }\r\n\r\n  /**\r\n   * Enable or disable debug logging\r\n   */\r\n  public setDebug(enabled: boolean): void {\r\n    (this.config as any).debug = enabled;\r\n    // Note: ConsoleLogger debug setting is read-only, create new logger if needed\r\n    if (enabled !== this.config.debug) {\r\n      this.logger.info(`Debug mode ${enabled ? \"enabled\" : \"disabled\"}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Test SDK connectivity\r\n   */\r\n  public async testConnection(): Promise<boolean> {\r\n    try {\r\n      // Try to make a simple request to test connectivity\r\n      const testUrl = `${this.config.apiBaseUrl}/v2.0/oa/getoa`;\r\n      await this.client.apiGet(testUrl, \"test-token\");\r\n      return true;\r\n    } catch (error) {\r\n      // We expect this to fail with authentication error, not network error\r\n      if (\r\n        (error as Error).message.includes(\"access_token\") ||\r\n        (error as Error).message.includes(\"401\")\r\n      ) {\r\n        return true; // Network is working, just auth failed as expected\r\n      }\r\n      this.logger.error(`Connection test failed: ${(error as Error).message}`);\r\n      return false;\r\n    }\r\n  }\r\n\r\n\r\n\r\n  /**\r\n   * Make a custom API request (advanced usage)\r\n   */\r\n  public async customRequest<T = any>(\r\n    method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\",\r\n    endpoint: string,\r\n    accessToken: string,\r\n    data?: any,\r\n    params?: Record<string, any>\r\n  ): Promise<T> {\r\n    switch (method) {\r\n      case \"GET\":\r\n        return this.client.apiGet<T>(endpoint, accessToken, params);\r\n      case \"POST\":\r\n        return this.client.apiPost<T>(endpoint, accessToken, data, params);\r\n      case \"PUT\":\r\n        return this.client.apiPut<T>(endpoint, accessToken, data, params);\r\n      case \"DELETE\":\r\n        return this.client.apiDelete<T>(endpoint, accessToken, params);\r\n      default:\r\n        throw new ZaloSDKError(`Unsupported HTTP method: ${method}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Upload file to Zalo API\r\n   */\r\n  public async uploadFile<T = any>(\r\n    endpoint: string,\r\n    accessToken: string,\r\n    file: Buffer | NodeJS.ReadableStream,\r\n    filename: string,\r\n    additionalFields?: Record<string, any>\r\n  ): Promise<T> {\r\n    return this.client.apiUploadFile<T>(\r\n      endpoint,\r\n      accessToken,\r\n      file,\r\n      filename,\r\n      additionalFields\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Dispose SDK resources\r\n   */\r\n  public dispose(): void {\r\n    this.logger.info(\"Zalo SDK disposed\");\r\n  }\r\n}\r\n"],"mappings":"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","names":["AuthScope","AuthMethod","GMFProductType","QuotaType","UserInfoFieldType","ZNSTemplateType","ZNSTemplateTag","ZNSButtonType","ZNSParamType","ArticleStatus","CommentStatus","ArticleType","CoverType","BodyItemType","VideoUploadStatus","PurchaseErrorCode","WebhookEventName","AttachmentType","axios","FormData","import_axios","import_form_data","FormData","axios","successful","failed","ReactionIcon","MiniAppQuotaType","QuotaOwnerType"]}