nextflow-zalo-sdk
Version:
SDK tích hợp Zalo toàn diện cho hệ thống CRM AI Nextflow
1 lines • 97 kB
JavaScript
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("events"),e=require("zca-js"),i=require("fs"),r=require("path"),n=require("axios"),s=require("crypto");function _interopNamespaceDefault(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(i){if("default"!==i){var r=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(e,i,r.get?r:{enumerable:!0,get:function(){return t[i]}})}}),e.default=t,Object.freeze(e)}var o,a=_interopNamespaceDefault(i),c=_interopNamespaceDefault(r);class NextflowZaloError extends Error{constructor(t,e={}){super(t),this.name="NextflowZaloError",this.code=e.code||"UNKNOWN_ERROR",this.statusCode=e.statusCode,this.details=e.details,this.cause=e.cause,this.timestamp=e.timestamp||Date.now(),Error.captureStackTrace&&Error.captureStackTrace(this,NextflowZaloError)}toJSON(){return{name:this.name,message:this.message,code:this.code,statusCode:this.statusCode,details:this.details,timestamp:this.timestamp,stack:this.stack}}static fromError(t,e={}){return new NextflowZaloError(t.message,{...e,cause:t})}static isNextflowZaloError(t){return t instanceof NextflowZaloError}}class AuthenticationError extends NextflowZaloError{constructor(t,e={}){super(t,{...e,code:e.code||"AUTHENTICATION_ERROR"}),this.name="AuthenticationError"}static loginFailed(t){return new AuthenticationError("Đăng nhập thất bại"+(t?`: ${t}`:""),{code:"LOGIN_FAILED"})}static tokenExpired(){return new AuthenticationError("Token đã hết hạn, vui lòng đăng nhập lại",{code:"TOKEN_EXPIRED"})}static invalidCredentials(){return new AuthenticationError("Thông tin đăng nhập không hợp lệ",{code:"INVALID_CREDENTIALS"})}static accessDenied(t){return new AuthenticationError("Không có quyền truy cập"+(t?` vào ${t}`:""),{code:"ACCESS_DENIED"})}static qrTimeout(){return new AuthenticationError("QR code đã hết hạn, vui lòng thử lại",{code:"QR_TIMEOUT",details:{suggestion:"Chạy lại lệnh để tạo QR code mới",timeout:"5 phút"}})}static qrCancelled(){return new AuthenticationError("QR login bị hủy bởi người dùng",{code:"QR_CANCELLED"})}static qrNotScanned(){return new AuthenticationError("QR code chưa được quét hoặc chưa được xác nhận",{code:"QR_NOT_SCANNED",details:{suggestion:"Vui lòng quét QR code bằng ứng dụng Zalo trên điện thoại"}})}static invalidSession(){return new AuthenticationError("Session không hợp lệ hoặc đã hết hạn",{code:"INVALID_SESSION",details:{suggestion:"Vui lòng đăng nhập lại"}})}static incompleteCredentials(t){return new AuthenticationError(`Thiếu thông tin đăng nhập: ${t.join(", ")}`,{code:"INCOMPLETE_CREDENTIALS",details:{missing:t,suggestion:"Kiểm tra file .env hoặc đăng nhập QR lại"}})}}class Logger{constructor(t={}){this.logs=[],this.level=t.level||"info",this.prefix=t.prefix,this.timestamp=!1!==t.timestamp,this.colors=!1!==t.colors}child(t){return new Logger({level:this.level,prefix:this.prefix?`${this.prefix}${t}`:t,timestamp:this.timestamp,colors:this.colors})}debug(t,e){this.log("debug",t,e)}info(t,e){this.log("info",t,e)}warn(t,e){this.log("warn",t,e)}error(t,e){this.log("error",t,e)}log(t,e,i){if(!this.shouldLog(t))return;const r={level:t,message:e,timestamp:Date.now(),prefix:this.prefix,data:i};this.logs.push(r),this.logs.length>1e3&&(this.logs=this.logs.slice(-500)),this.outputToConsole(r)}shouldLog(t){return Logger.LEVELS[t]>=Logger.LEVELS[this.level]}outputToConsole(t){let e="";if(this.timestamp){e+=`[${new Date(t.timestamp).toISOString()}] `}if(this.colors){const i=Logger.COLORS[t.level],r=Logger.COLORS.reset;e+=`${i}${t.level.toUpperCase()}${r} `}else e+=`${t.level.toUpperCase()} `;t.prefix&&(e+=`${t.prefix} `),e+=t.message,t.level}setLevel(t){this.level=t}getLevel(){return this.level}getLogs(){return[...this.logs]}getLogsByLevel(t){return this.logs.filter(e=>e.level===t)}getLogsByTimeRange(t,e){return this.logs.filter(i=>i.timestamp>=t&&i.timestamp<=e)}clearLogs(){this.logs=[]}exportLogs(){return JSON.stringify(this.logs,null,2)}static create(t={}){return new Logger(t)}static default(){return new Logger({level:"info",prefix:"[NextflowZaloSDK]",timestamp:!0,colors:!0})}}Logger.LEVELS={debug:0,info:1,warn:2,error:3},Logger.COLORS={debug:"[36m",info:"[32m",warn:"[33m",error:"[31m",reset:"[0m"};class QRLoginHelper{constructor(t){this.events=[],this.startTime=0,this.logger=t||Logger.create({prefix:"[QRHelper]"})}validateOptions(t={}){const e={qrPath:t.qrPath||this.getDefaultQRPath(),userAgent:t.userAgent||this.getDefaultUserAgent(),language:t.language||"vi"};return e.qrPath&&this.ensureQRDirectory(e.qrPath),this.isValidUserAgent(e.userAgent)||this.logger.warn("⚠️ User Agent có thể không hợp lệ:",e.userAgent),["vi","en"].includes(e.language)||(this.logger.warn("⚠️ Ngôn ngữ không được hỗ trợ, sử dụng mặc định: vi"),e.language="vi"),this.logger.debug("✅ QR Options validated:",e),e}startSession(){this.startTime=Date.now(),this.events=[],this.logger.info("🚀 Bắt đầu QR login session")}recordEvent(t,e,i){const r={type:t,data:e,error:i,timestamp:Date.now()};return this.events.push(r),this.logger.debug(`📊 QR Event recorded: ${t}`,e),r}endSession(t,e,i){const r=Date.now()-this.startTime,n={success:t,api:e,error:i,duration:r,events:[...this.events]};return this.logger.info(`🏁 QR login session kết thúc: ${t?"thành công":"thất bại"} (${r}ms)`),t?this.logger.info("✅ QR Login thành công"):this.logger.error("❌ QR Login thất bại:",i?.message),n}createTimeoutPromise(t=3e5){return new Promise((e,i)=>{setTimeout(()=>{this.recordEvent("timeout",{timeoutMs:t}),i(AuthenticationError.qrTimeout())},t)})}handleQREvent(t){const e=this.recordEvent(t.type,t.data,t.error);switch(t.type){case"qr_generated":this.logger.info("✅ QR Code đã được tạo"),t.qrPath&&(this.logger.info(`📄 QR Code saved to: ${t.qrPath}`),this.displayQRInfo(t.qrPath));break;case"qr_scanned":this.logger.info("📱 QR Code đã được quét"),this.logger.info("⏳ Đang chờ xác nhận trên điện thoại...");break;case"login_success":this.logger.info("🎉 Đăng nhập thành công!");break;case"error":this.logger.error("❌ QR Login Error:",t.error);break;case"timeout":this.logger.error("⏰ QR Login timeout");break;default:this.logger.debug(`📋 QR Event: ${t.type}`,t.data)}return e}displayQRInfo(t){try{const e=a.statSync(t);this.logger.info("📊 QR Code info:"),this.logger.info(` - File: ${t}`),this.logger.info(` - Size: ${e.size} bytes`),this.logger.info(` - Created: ${e.birthtime.toLocaleString()}`),this.logger.info("📱 Vui lòng mở ứng dụng Zalo và quét QR code")}catch(t){this.logger.warn("⚠️ Không thể đọc thông tin QR file:",t)}}getDefaultQRPath(){return`./qr-codes/qr-${(new Date).toISOString().replace(/[:.]/g,"-")}.png`}getDefaultUserAgent(){return"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"}isValidUserAgent(t){return t.length>10&&t.includes("Mozilla")&&(t.includes("Chrome")||t.includes("Firefox")||t.includes("Safari"))}ensureQRDirectory(t){const e=c.dirname(t);if(!a.existsSync(e))try{a.mkdirSync(e,{recursive:!0}),this.logger.info(`📁 Đã tạo thư mục QR: ${e}`)}catch(t){throw this.logger.error("❌ Không thể tạo thư mục QR:",t),new AuthenticationError(`Không thể tạo thư mục QR: ${e}`,{cause:t})}}cleanupOldQRFiles(t="./qr-codes",e=864e5){try{if(!a.existsSync(t))return;const i=a.readdirSync(t),r=Date.now();let n=0;i.forEach(i=>{const s=c.join(t,i),o=a.statSync(s);r-o.birthtime.getTime()>e&&(a.unlinkSync(s),n++)}),n>0&&this.logger.info(`🧹 Đã xóa ${n} QR files cũ`)}catch(t){this.logger.warn("⚠️ Lỗi cleanup QR files:",t)}}generateReport(){const t=Date.now()-this.startTime,e=this.events.reduce((t,e)=>(t[e.type]=(t[e.type]||0)+1,t),{});let i="\n📊 QR Login Session Report\n";return i+="================================\n",i+=`Duration: ${t}ms\n`,i+=`Total Events: ${this.events.length}\n`,i+="\nEvent Breakdown:\n",Object.entries(e).forEach(([t,e])=>{i+=` - ${t}: ${e}\n`}),i+="\nTimeline:\n",this.events.forEach((t,e)=>{const r=t.timestamp-this.startTime;i+=` ${e+1}. [${r}ms] ${t.type}\n`}),i}}!function(t){t.LOGIN_SUCCESS="login_success",t.LOGIN_FAILED="login_failed",t.LOGOUT="logout",t.MESSAGE_RECEIVED="message_received",t.MESSAGE_SENT="message_sent",t.MESSAGE_FAILED="message_failed",t.CONTACT_ADDED="contact_added",t.CONTACT_UPDATED="contact_updated",t.CONTACT_REMOVED="contact_removed",t.CONTACT_BLOCKED="contact_blocked",t.CONNECTION_CHANGED="connection_changed",t.CONNECTION_LOST="connection_lost",t.CONNECTION_RESTORED="connection_restored",t.QR_GENERATED="qr_generated",t.QR_SCANNED="qr_scanned",t.QR_EXPIRED="qr_expired",t.SYNC_STARTED="sync_started",t.SYNC_PROGRESS="sync_progress",t.SYNC_COMPLETED="sync_completed",t.SYNC_FAILED="sync_failed",t.WEBHOOK_RECEIVED="webhook_received",t.WEBHOOK_PROCESSED="webhook_processed",t.ERROR_OCCURRED="error_occurred",t.ANALYTICS_DATA="analytics_data"}(o||(o={}));class EnhancedEventEmitter extends t.EventEmitter{constructor(t){super(),this.eventHistory=[],this.maxHistorySize=1e3,this.logger=t||Logger.create({prefix:"[EventEmitter]"}),this.setMaxListeners(50)}emitLoginSuccess(t,e){const i={...this.createBaseEvent(t),...e};this.logger.info(`🎉 Login success: ${e.method} - User: ${e.user.name||e.user.id}`),this.emitWithHistory(o.LOGIN_SUCCESS,i)}emitMessage(t,e){const i={...this.createBaseEvent(t),...e};this.logger.info(`💬 Message ${e.direction}: ${e.message.type} from ${e.message.from_user}`),"incoming"===e.direction?this.emitWithHistory(o.MESSAGE_RECEIVED,i):this.emitWithHistory(o.MESSAGE_SENT,i)}emitContact(t,e){const i={...this.createBaseEvent(t),...e};switch(this.logger.info(`👤 Contact ${e.action}: ${e.contact.name} (${e.contact.id})`),e.action){case"added":this.emitWithHistory(o.CONTACT_ADDED,i);break;case"updated":this.emitWithHistory(o.CONTACT_UPDATED,i);break;case"removed":this.emitWithHistory(o.CONTACT_REMOVED,i);break;case"blocked":this.emitWithHistory(o.CONTACT_BLOCKED,i)}}emitConnectionChange(t,e){const i={...this.createBaseEvent(t),...e};this.logger.info(`🔗 Connection changed: ${e.changed_services.join(", ")}`),this.emitWithHistory(o.CONNECTION_CHANGED,i)}emitQREvent(t,e){const i={...this.createBaseEvent(t),...e};switch(this.logger.info(`📱 QR ${e.qr.type}: ${e.qr.file_path||"in-memory"}`),e.qr.type){case"generated":this.emitWithHistory(o.QR_GENERATED,i);break;case"scanned":this.emitWithHistory(o.QR_SCANNED,i);break;case"expired":this.emitWithHistory(o.QR_EXPIRED,i)}}emitSync(t,e){const i={...this.createBaseEvent(t),...e};switch(this.logger.info(`🔄 Sync ${e.sync.status}: ${e.sync.type} with ${e.target_system}`),e.sync.status){case"started":this.emitWithHistory(o.SYNC_STARTED,i);break;case"in_progress":this.emitWithHistory(o.SYNC_PROGRESS,i);break;case"completed":this.emitWithHistory(o.SYNC_COMPLETED,i);break;case"failed":this.emitWithHistory(o.SYNC_FAILED,i)}}emitError(t,e){const i={...this.createBaseEvent(t),...e};this.logger.error(`❌ Error in ${e.context.service}.${e.context.operation}: ${e.error.message}`),this.emitWithHistory(o.ERROR_OCCURRED,i)}getEventHistory(t){return t?this.eventHistory.slice(-t):[...this.eventHistory]}getEventsByType(t){return this.eventHistory.filter(e=>{switch(t){case o.LOGIN_SUCCESS:return"method"in e&&"user"in e;case o.MESSAGE_RECEIVED:case o.MESSAGE_SENT:return"message"in e&&"direction"in e;case o.CONTACT_ADDED:case o.CONTACT_UPDATED:case o.CONTACT_REMOVED:case o.CONTACT_BLOCKED:return"contact"in e&&"action"in e;case o.CONNECTION_CHANGED:return"connection"in e&&"changed_services"in e;case o.QR_GENERATED:case o.QR_SCANNED:case o.QR_EXPIRED:return"qr"in e;case o.SYNC_STARTED:case o.SYNC_PROGRESS:case o.SYNC_COMPLETED:case o.SYNC_FAILED:return"sync"in e&&"target_system"in e;case o.ERROR_OCCURRED:return"error"in e&&"context"in e;default:return!1}})}clearHistory(){this.eventHistory=[],this.logger.info("🧹 Event history cleared")}getEventStats(){const t={};return this.eventHistory.forEach(e=>{let i="unknown";"method"in e?i="login":"message"in e?i="message":"contact"in e?i="contact":"connection"in e?i="connection":"qr"in e?i="qr":"sync"in e?i="sync":"error"in e&&(i="error"),t[i]=(t[i]||0)+1}),t}createBaseEvent(t){return{timestamp:Date.now(),source:t,event_id:this.generateEventId()}}emitWithHistory(t,e){this.eventHistory.push(e),this.eventHistory.length>this.maxHistorySize&&(this.eventHistory=this.eventHistory.slice(-this.maxHistorySize)),this.emit(t,e),this.emit("event",{name:t,data:e})}generateEventId(){return`evt_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}setMaxHistorySize(t){this.maxHistorySize=t,this.eventHistory.length>t&&(this.eventHistory=this.eventHistory.slice(-t))}}class PersonalAuth{constructor(t,e){this.client=t,this.logger=e,this.qrHelper=new QRLoginHelper(this.logger.child("[QRHelper]")),this.eventEmitter=new EnhancedEventEmitter(this.logger.child("[Events]"))}async loginQR(t={}){try{this.logger.info("🚀 Bắt đầu quá trình đăng nhập bằng QR code..."),this.logger.info("📱 Vui lòng quét mã QR bằng ứng dụng Zalo trên điện thoại của bạn"),this.qrHelper.startSession();const e=this.qrHelper.validateOptions(t),i=this.client.getZaloInstance();if(!i)throw new AuthenticationError("Zalo instance chưa được khởi tạo - vui lòng gọi client.initialize() trước");const r=this.qrHelper.createTimeoutPromise(),n=new Promise((t,r)=>{i.loginQR(e,e=>{try{this.qrHelper.handleQREvent(e),this.client.emit("qr_event",e),"login_success"===e.type&&e.api&&t(e.api)}catch(t){r(t)}}).then(e=>{e&&t(e)}).catch(r)}),s=await Promise.race([n,r]);this.client.api=s,this.client._isConnected=!0,this.logger.info("✅ Đăng nhập QR thành công"),this.logger.info("💾 Gọi saveCredentials() để lưu thông tin đăng nhập");const o=this.qrHelper.endSession(!0,s);let a={id:"unknown",name:void 0};try{const t=s.getContext();if(a.id=t.imei||"unknown",s.getCurrentUser){const t=await s.getCurrentUser();a.name=t?.name||t?.displayName}}catch(t){this.logger.warn("Không thể lấy user info:",t)}this.eventEmitter.emitLoginSuccess("personal",{method:"qr",user:a,credentials:{imei:s.getContext()?.imei||"unknown",userAgent:e.userAgent||"unknown",cookie:s.getContext()?.cookie},session:{login_timestamp:Date.now(),status:"active"}}),this.client.emit("login_success",{method:"qr",timestamp:Date.now(),result:o}),this.logger.debug(this.qrHelper.generateReport())}catch(t){if(this.qrHelper.endSession(!1,void 0,t),this.logger.error("❌ Lỗi đăng nhập QR:",t),t instanceof AuthenticationError)throw t;if(t instanceof Error){if(t.message.includes("timeout"))throw AuthenticationError.qrTimeout();if(t.message.includes("network"))throw new AuthenticationError("Lỗi mạng - Kiểm tra kết nối internet",{cause:t});if(t.message.includes("cancelled"))throw AuthenticationError.qrCancelled()}throw AuthenticationError.loginFailed(t.message)}}cleanupQRFiles(){this.qrHelper.cleanupOldQRFiles()}async loginCredentials(t){try{this.logger.info("Bắt đầu đăng nhập credentials...");const e=this.client.getAPI();await e.login(t),this.logger.info("Đăng nhập credentials thành công")}catch(t){throw this.logger.error("Lỗi đăng nhập credentials:",t),AuthenticationError.invalidCredentials()}}async getSession(){try{const t=this.client.getAPI();return await t.getSession()}catch(t){throw AuthenticationError.tokenExpired()}}async refreshSession(){try{const t=this.client.getAPI();await t.refreshSession(),this.logger.info("Session đã được refresh")}catch(t){throw this.logger.error("Lỗi refresh session:",t),AuthenticationError.tokenExpired()}}async saveCredentials(t=".env"){try{this.logger.info("Đang lưu credentials vào .env...");const e=this.client.getAPI(),i=await e.getSession();if(!i||!i.cookie)throw new Error("Không có session data để lưu");let r="";a.existsSync(t)&&(r=a.readFileSync(t,"utf8"));const n={ZALO_PERSONAL_IMEI:i.imei||"auto-generated-imei",ZALO_PERSONAL_USER_AGENT:i.userAgent||"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",ZALO_PERSONAL_COOKIE:JSON.stringify(i.cookie)};Object.entries(n).forEach(([t,e])=>{const i=new RegExp(`^${t}=.*$`,"m"),n=`${t}=${e}`;i.test(r)?r=r.replace(i,n):r+=`\n${n}`}),a.writeFileSync(t,r.trim()+"\n"),this.logger.info(`✅ Đã lưu credentials vào ${t}`),this.logger.info("🔄 Khởi động lại ứng dụng để sử dụng credentials mới")}catch(t){throw this.logger.error("❌ Lỗi lưu credentials:",t),new AuthenticationError("Lưu credentials thất bại",{cause:t})}}async logout(){try{const t=this.client.getAPI();await t.logout(),this.logger.info("Đã đăng xuất")}catch(t){throw this.logger.error("Lỗi đăng xuất:",t),t}}}class ValidationError extends NextflowZaloError{constructor(t,e={}){super(t,{...e,code:e.code||"VALIDATION_ERROR"}),this.name="ValidationError",this.field=e.field,this.value=e.value,this.expectedType=e.expectedType,this.constraints=e.constraints}static required(t){return new ValidationError(`Trường '${t}' là bắt buộc`,{code:"FIELD_REQUIRED",field:t})}static invalidType(t,e,i){return new ValidationError(`Trường '${t}' phải là ${i}, nhận được ${typeof e}`,{code:"INVALID_TYPE",field:t,value:e,expectedType:i})}static invalidValue(t,e,i){return new ValidationError(`Giá trị '${e}' của trường '${t}' không hợp lệ. ${i.join(", ")}`,{code:"INVALID_VALUE",field:t,value:e,constraints:i})}static invalidLength(t,e,i,r){let n=`Độ dài trường '${t}' không hợp lệ (${e.length} ký tự)`;return void 0!==i&&void 0!==r?n+=`. Phải từ ${i} đến ${r} ký tự`:void 0!==i?n+=`. Tối thiểu ${i} ký tự`:void 0!==r&&(n+=`. Tối đa ${r} ký tự`),new ValidationError(n,{code:"INVALID_LENGTH",field:t,value:e,constraints:[`min: ${i}`,`max: ${r}`].filter(Boolean)})}static invalidFormat(t,e,i){return new ValidationError(`Trường '${t}' không đúng định dạng. Mong đợi: ${i}`,{code:"INVALID_FORMAT",field:t,value:e,expectedType:i})}static invalidEmail(t){return new ValidationError(`Email '${t}' không hợp lệ`,{code:"INVALID_EMAIL",field:"email",value:t,expectedType:"valid email format"})}static invalidUrl(t){return new ValidationError(`URL '${t}' không hợp lệ`,{code:"INVALID_URL",field:"url",value:t,expectedType:"valid URL format"})}static invalidPhone(t){return new ValidationError(`Số điện thoại '${t}' không hợp lệ`,{code:"INVALID_PHONE",field:"phone",value:t,expectedType:"valid phone number"})}toJSON(){return{...super.toJSON(),field:this.field,value:this.value,expectedType:this.expectedType,constraints:this.constraints}}}class NetworkError extends NextflowZaloError{constructor(t,e={}){super(t,{...e,code:e.code||"NETWORK_ERROR"}),this.name="NetworkError",this.url=e.url,this.method=e.method,this.timeout=e.timeout,this.retryCount=e.retryCount}static timeout(t,e){return new NetworkError(`Request timeout sau ${e}ms: ${t}`,{code:"REQUEST_TIMEOUT",url:t,timeout:e})}static connectionFailed(t,e){return new NetworkError(`Kết nối thất bại đến ${t}${e?`: ${e}`:""}`,{code:"CONNECTION_FAILED",url:t})}static dnsError(t){return new NetworkError(`Không thể phân giải DNS cho ${t}`,{code:"DNS_ERROR",url:t})}static sslError(t,e){return new NetworkError(`Lỗi SSL/TLS khi kết nối ${t}${e?`: ${e}`:""}`,{code:"SSL_ERROR",url:t})}static httpError(t,e,i){return new NetworkError(`HTTP ${e}${i?` ${i}`:""}: ${t}`,{code:"HTTP_ERROR",url:t,statusCode:e})}static retryExhausted(t,e){return new NetworkError(`Đã thử ${e} lần nhưng vẫn thất bại: ${t}`,{code:"RETRY_EXHAUSTED",url:t,retryCount:e})}isRetryable(){return["REQUEST_TIMEOUT","CONNECTION_FAILED","DNS_ERROR"].includes(this.code)}toJSON(){return{...super.toJSON(),url:this.url,method:this.method,timeout:this.timeout,retryCount:this.retryCount}}}class PersonalMessaging{constructor(t,e){this.client=t,this.logger=e}async sendText(t,e,i){return this.validateThreadId(t),this.validateTextContent(e),this.client.apiCall(async()=>{try{const r=this.client.getAPI(),n=await r.sendMessage({msg:e,quote:i?.quote},t,1);return this.logger.info(`📤 Gửi tin nhắn text thành công: ${t}`),{messageId:n.msgId,timestamp:n.ts||Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi tin nhắn text:",t),new NetworkError("Gửi tin nhắn thất bại",{cause:t})}},`message_${t}`)}async sendSticker(t,e){if(this.validateThreadId(t),!e)throw ValidationError.required("stickerId");return this.client.apiCall(async()=>{try{const i=this.client.getAPI(),r=await i.sendSticker(e,t,1);return this.logger.info(`Gửi sticker thành công: ${t}`),{messageId:r.msgId,timestamp:r.ts||Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi sticker:",t),new NetworkError("Gửi sticker thất bại",{cause:t})}},`sticker_${t}`)}async sendImage(t,e){if(this.validateThreadId(t),!e)throw ValidationError.required("imagePath");return this.client.apiCall(async()=>{try{const i=this.client.getAPI(),r=await i.sendImage(e,t,1);return this.logger.info(`Gửi hình ảnh thành công: ${t}`),{messageId:r.msgId,timestamp:r.ts||Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi hình ảnh:",t),new NetworkError("Gửi hình ảnh thất bại",{cause:t})}},`image_${t}`)}async sendFile(t,e){if(this.validateThreadId(t),!e)throw ValidationError.required("filePath");return this.client.apiCall(async()=>{try{const i=this.client.getAPI(),r=await i.sendFile(e,t,1);return this.logger.info(`Gửi file thành công: ${t}`),{messageId:r.msgId,timestamp:r.ts||Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi file:",t),new NetworkError("Gửi file thất bại",{cause:t})}},`file_${t}`)}async replyMessage(t,e,i){if(this.validateThreadId(i),this.validateTextContent(e),!t)throw ValidationError.required("messageId");return this.client.apiCall(async()=>{try{const r=this.client.getAPI(),n={msgId:t,msg:e},s=await r.sendMessage({msg:e,quote:n},i,1);return this.logger.info(`Reply tin nhắn thành công: ${i}`),{messageId:s.msgId,timestamp:s.ts||Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi reply tin nhắn:",t),new NetworkError("Reply tin nhắn thất bại",{cause:t})}},`reply_${i}`)}async deleteMessage(t,e){if(!t)throw ValidationError.required("messageId");return this.validateThreadId(e),this.client.apiCall(async()=>{try{const i=this.client.getAPI();return await i.deleteMessage(t,e),this.logger.info(`Xóa tin nhắn thành công: ${t}`),!0}catch(t){throw this.logger.error("Lỗi xóa tin nhắn:",t),new NetworkError("Xóa tin nhắn thất bại",{cause:t})}},`delete_${e}`)}async addReaction(t,e,i){if(!t)throw ValidationError.required("messageId");if(this.validateThreadId(e),!i)throw ValidationError.required("reaction");return this.client.apiCall(async()=>{try{const r=this.client.getAPI();return await r.addReaction(t,e,i),this.logger.info(`Thêm reaction thành công: ${t}`),!0}catch(t){throw this.logger.error("Lỗi thêm reaction:",t),new NetworkError("Thêm reaction thất bại",{cause:t})}},`reaction_${e}`)}async markAsRead(t,e){if(!t)throw ValidationError.required("messageId");return this.validateThreadId(e),this.client.apiCall(async()=>{try{const i=this.client.getAPI();return await i.markAsRead(t,e),this.logger.debug(`Đánh dấu đã đọc: ${t}`),!0}catch(t){return this.logger.error("Lỗi đánh dấu đã đọc:",t),!1}},`read_${e}`)}validateThreadId(t){if(!t)throw ValidationError.required("threadId");if("string"!=typeof t)throw ValidationError.invalidType("threadId",t,"string")}validateTextContent(t){if(!t)throw ValidationError.required("content");if("string"!=typeof t)throw ValidationError.invalidType("content",t,"string");if(t.length>5e3)throw ValidationError.invalidLength("content",t,void 0,5e3)}}class PersonalContacts{constructor(t,e){this.client=t,this.logger=e}async getFriends(){return this.client.apiCall(async()=>{try{const t=this.client.getAPI(),e=await t.getAllFriends();return this.logger.info(`📱 Lấy danh sách ${e.length} bạn bè thành công`),e.map(t=>this.transformContact(t))}catch(t){throw this.logger.error("❌ Lỗi lấy danh sách bạn bè:",t),new NetworkError("Lấy danh sách bạn bè thất bại",{cause:t})}},"get_friends")}async getFriend(t){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI(),i=await e.getUserInfo(t);return this.logger.info(`👤 Lấy thông tin bạn bè: ${t}`),this.transformContact(i)}catch(t){throw this.logger.error("❌ Lỗi lấy thông tin bạn bè:",t),new NetworkError("Lấy thông tin bạn bè thất bại",{cause:t})}},`get_friend_${t}`)}async sendFriendRequest(t,e){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const i=this.client.getAPI();return await i.sendFriendRequest(t,e),this.logger.info(`📤 Gửi lời mời kết bạn: ${t}${e?` với tin nhắn: "${e}"`:""}`),!0}catch(t){throw this.logger.error("❌ Lỗi gửi lời mời kết bạn:",t),new NetworkError("Gửi lời mời kết bạn thất bại",{cause:t})}},`friend_request_${t}`)}async acceptFriendRequest(t){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI();return await e.acceptFriendRequest(t),this.logger.info(`Chấp nhận lời mời kết bạn: ${t}`),!0}catch(t){throw this.logger.error("Lỗi chấp nhận lời mời kết bạn:",t),new NetworkError("Chấp nhận lời mời kết bạn thất bại",{cause:t})}},`accept_friend_${t}`)}async rejectFriendRequest(t){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI();return await e.rejectFriendRequest(t),this.logger.info(`Từ chối lời mời kết bạn: ${t}`),!0}catch(t){throw this.logger.error("Lỗi từ chối lời mời kết bạn:",t),new NetworkError("Từ chối lời mời kết bạn thất bại",{cause:t})}},`reject_friend_${t}`)}async removeFriend(t){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI();return await e.removeFriend(t),this.logger.info(`Xóa bạn bè: ${t}`),!0}catch(t){throw this.logger.error("Lỗi xóa bạn bè:",t),new NetworkError("Xóa bạn bè thất bại",{cause:t})}},`remove_friend_${t}`)}async blockUser(t){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI();return await e.blockUser(t),this.logger.info(`Chặn người dùng: ${t}`),!0}catch(t){throw this.logger.error("Lỗi chặn người dùng:",t),new NetworkError("Chặn người dùng thất bại",{cause:t})}},`block_user_${t}`)}async unblockUser(t){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI();return await e.unblockUser(t),this.logger.info(`Bỏ chặn người dùng: ${t}`),!0}catch(t){throw this.logger.error("Lỗi bỏ chặn người dùng:",t),new NetworkError("Bỏ chặn người dùng thất bại",{cause:t})}},`unblock_user_${t}`)}async searchUsers(t){if(!t)throw ValidationError.required("query");if(t.length<2)throw ValidationError.invalidLength("query",t,2);return this.client.apiCall(async()=>{try{const e=this.client.getAPI(),i=await e.searchUsers(t);return this.logger.info(`Tìm kiếm người dùng: ${t} (${i.length} kết quả)`),i.map(t=>this.transformContact(t))}catch(t){throw this.logger.error("Lỗi tìm kiếm người dùng:",t),new NetworkError("Tìm kiếm người dùng thất bại",{cause:t})}},`search_users_${t}`)}transformContact(t){return{id:t.userId||t.uid,name:t.name||t.displayName,displayName:t.displayName||t.name,avatar:t.avatar,phone:t.phoneNumber,status:t.status||"offline",lastSeen:t.lastSeen,alias:t.alias,isBlocked:t.isBlocked||!1,isFriend:!1!==t.isFriend}}}class PersonalGroups{constructor(t,e){this.client=t,this.logger=e}async getGroups(){return this.client.apiCall(async()=>{try{const t=this.client.getAPI(),e=await t.getAllGroups();return this.logger.info(`👥 Lấy danh sách ${e.length} nhóm thành công`),e.map(t=>this.transformGroup(t))}catch(t){throw this.logger.error("❌ Lỗi lấy danh sách nhóm:",t),new NetworkError("Lấy danh sách nhóm thất bại",{cause:t})}},"get_groups")}async getGroup(t){if(!t)throw ValidationError.required("groupId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI(),i=await e.getGroupInfo(t);return this.logger.info(`Lấy thông tin nhóm: ${t}`),this.transformGroup(i)}catch(t){throw this.logger.error("Lỗi lấy thông tin nhóm:",t),new NetworkError("Lấy thông tin nhóm thất bại",{cause:t})}},`get_group_${t}`)}async createGroup(t){return this.validateCreateGroupOptions(t),this.client.apiCall(async()=>{try{const e=this.client.getAPI(),i=await e.createGroup(t.name,t.memberIds);return this.logger.info(`Tạo nhóm mới: ${t.name}`),this.transformGroup(i)}catch(t){throw this.logger.error("Lỗi tạo nhóm:",t),new NetworkError("Tạo nhóm thất bại",{cause:t})}},"create_group")}async addMembers(t,e){if(!t)throw ValidationError.required("groupId");if(!e||0===e.length)throw ValidationError.required("userIds");return this.client.apiCall(async()=>{try{const i=this.client.getAPI();return await i.addUserToGroup(t,e),this.logger.info(`Thêm ${e.length} thành viên vào nhóm: ${t}`),!0}catch(t){throw this.logger.error("Lỗi thêm thành viên:",t),new NetworkError("Thêm thành viên thất bại",{cause:t})}},`add_members_${t}`)}async removeMembers(t,e){if(!t)throw ValidationError.required("groupId");if(!e||0===e.length)throw ValidationError.required("userIds");return this.client.apiCall(async()=>{try{const i=this.client.getAPI();return await i.removeUserFromGroup(t,e),this.logger.info(`Xóa ${e.length} thành viên khỏi nhóm: ${t}`),!0}catch(t){throw this.logger.error("Lỗi xóa thành viên:",t),new NetworkError("Xóa thành viên thất bại",{cause:t})}},`remove_members_${t}`)}async leaveGroup(t){if(!t)throw ValidationError.required("groupId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI();return await e.leaveGroup(t),this.logger.info(`Rời khỏi nhóm: ${t}`),!0}catch(t){throw this.logger.error("Lỗi rời khỏi nhóm:",t),new NetworkError("Rời khỏi nhóm thất bại",{cause:t})}},`leave_group_${t}`)}async updateGroupName(t,e){if(!t)throw ValidationError.required("groupId");if(!e)throw ValidationError.required("name");if(e.length>100)throw ValidationError.invalidLength("name",e,void 0,100);return this.client.apiCall(async()=>{try{const i=this.client.getAPI();return await i.updateGroupName(t,e),this.logger.info(`Cập nhật tên nhóm: ${t} -> ${e}`),!0}catch(t){throw this.logger.error("Lỗi cập nhật tên nhóm:",t),new NetworkError("Cập nhật tên nhóm thất bại",{cause:t})}},`update_group_name_${t}`)}async updateGroupAvatar(t,e){if(!t)throw ValidationError.required("groupId");if(!e)throw ValidationError.required("avatarPath");return this.client.apiCall(async()=>{try{const i=this.client.getAPI();return await i.updateGroupAvatar(t,e),this.logger.info(`Cập nhật avatar nhóm: ${t}`),!0}catch(t){throw this.logger.error("Lỗi cập nhật avatar nhóm:",t),new NetworkError("Cập nhật avatar nhóm thất bại",{cause:t})}},`update_group_avatar_${t}`)}async createInviteLink(t){if(!t)throw ValidationError.required("groupId");return this.client.apiCall(async()=>{try{const e=this.client.getAPI(),i=await e.createInviteLink(t);return this.logger.info(`Tạo link mời nhóm: ${t}`),i}catch(t){throw this.logger.error("Lỗi tạo link mời:",t),new NetworkError("Tạo link mời thất bại",{cause:t})}},`create_invite_link_${t}`)}validateCreateGroupOptions(t){if(!t.name)throw ValidationError.required("name");if(t.name.length>100)throw ValidationError.invalidLength("name",t.name,void 0,100);if(!t.memberIds||0===t.memberIds.length)throw ValidationError.required("memberIds");if(t.memberIds.length>100)throw ValidationError.invalidValue("memberIds",t.memberIds,["Tối đa 100 thành viên"])}transformGroup(t){return{id:t.groupId||t.id,name:t.groupName||t.name,avatar:t.avatar,description:t.description,memberCount:t.memberCount||t.members?.length||0,members:t.members?t.members.map(t=>this.transformMember(t)):[],settings:{allowMemberInvite:!1!==t.settings?.allowMemberInvite,allowMemberChangeInfo:!1!==t.settings?.allowMemberChangeInfo,joinApprovalRequired:!0===t.settings?.joinApprovalRequired},createdAt:t.createdTime||Date.now(),ownerId:t.ownerId||t.creatorId,adminIds:t.adminIds||[]}}transformMember(t){return{id:t.userId||t.uid,name:t.name||t.displayName,avatar:t.avatar,role:t.role||"member",joinedAt:t.joinTime||Date.now()}}}class RateLimitError extends NextflowZaloError{constructor(t,e={}){super(t,{...e,code:e.code||"RATE_LIMIT_EXCEEDED"}),this.name="RateLimitError",this.limit=e.limit,this.remaining=e.remaining,this.resetTime=e.resetTime,this.retryAfter=e.retryAfter}static messageLimit(t,e){const i=Math.ceil((e-Date.now())/1e3);return new RateLimitError(`Vượt quá giới hạn ${t} tin nhắn. Thử lại sau ${i} giây`,{code:"MESSAGE_RATE_LIMIT",limit:t,remaining:0,resetTime:e,retryAfter:i})}static apiLimit(t,e,i){const r=Math.ceil((i-Date.now())/1e3);return new RateLimitError(`Vượt quá giới hạn API ${t} requests. Còn lại ${e}. Reset sau ${r} giây`,{code:"API_RATE_LIMIT",limit:t,remaining:e,resetTime:i,retryAfter:r})}static temporaryBlock(t){return new RateLimitError(`Tạm thời bị chặn trong ${t} giây do vi phạm giới hạn`,{code:"TEMPORARY_BLOCK",retryAfter:t})}getRetryAfterSeconds(){return this.retryAfter?this.retryAfter:this.resetTime?Math.max(0,Math.ceil((this.resetTime-Date.now())/1e3)):60}toJSON(){return{...super.toJSON(),limit:this.limit,remaining:this.remaining,resetTime:this.resetTime,retryAfter:this.retryAfter}}}class RateLimiter{constructor(t){this.requests=new Map,this.config={maxRequests:t.maxRequests,windowMs:t.windowMs,skipSuccessfulRequests:t.skipSuccessfulRequests??!1,skipFailedRequests:t.skipFailedRequests??!0},this.cleanupInterval=setInterval(()=>{this.cleanup()},6e4)}async checkLimit(t="default"){const e=Date.now(),i=this.getOrCreateEntry(t);if(i.blocked&&i.blockUntil&&e<i.blockUntil){const t=Math.ceil((i.blockUntil-e)/1e3);throw RateLimitError.temporaryBlock(t)}i.blocked&&i.blockUntil&&e>=i.blockUntil&&(i.blocked=!1,i.blockUntil=void 0);const r=e-this.config.windowMs;if(i.timestamps=i.timestamps.filter(t=>t>r),i.timestamps.length>=this.config.maxRequests){const t=Math.min(...i.timestamps)+this.config.windowMs;throw RateLimitError.apiLimit(this.config.maxRequests,0,t)}return i.timestamps.push(e),{limit:this.config.maxRequests,remaining:this.config.maxRequests-i.timestamps.length,resetTime:e+this.config.windowMs}}recordSuccess(t="default"){this.config.skipSuccessfulRequests&&this.removeLastRequest(t)}recordFailure(t="default"){this.config.skipFailedRequests&&this.removeLastRequest(t)}blockKey(t,e){const i=this.getOrCreateEntry(t);i.blocked=!0,i.blockUntil=Date.now()+e}unblockKey(t){const e=this.requests.get(t);e&&(e.blocked=!1,e.blockUntil=void 0)}getInfo(t="default"){const e=Date.now(),i=this.requests.get(t);if(!i)return{limit:this.config.maxRequests,remaining:this.config.maxRequests,resetTime:e+this.config.windowMs};const r=e-this.config.windowMs,n=i.timestamps.filter(t=>t>r),s=Math.max(0,this.config.maxRequests-n.length),o=n.length>0?Math.min(...n)+this.config.windowMs:e+this.config.windowMs,a={limit:this.config.maxRequests,remaining:s,resetTime:o};return i.blocked&&i.blockUntil&&(a.retryAfter=Math.ceil((i.blockUntil-e)/1e3)),a}reset(t="default"){this.requests.delete(t)}resetAll(){this.requests.clear()}getKeys(){return Array.from(this.requests.keys())}isBlocked(t="default"){const e=this.requests.get(t);return!(!e||!e.blocked)&&(!(e.blockUntil&&Date.now()>=e.blockUntil)||(e.blocked=!1,e.blockUntil=void 0,!1))}destroy(){clearInterval(this.cleanupInterval),this.requests.clear()}getOrCreateEntry(t){let e=this.requests.get(t);return e||(e={timestamps:[],blocked:!1},this.requests.set(t,e)),e}removeLastRequest(t){const e=this.requests.get(t);e&&e.timestamps.length>0&&e.timestamps.pop()}cleanup(){const t=Date.now(),e=t-this.config.windowMs;for(const[i,r]of this.requests.entries())r.timestamps=r.timestamps.filter(t=>t>e),r.blocked&&r.blockUntil&&t>=r.blockUntil&&(r.blocked=!1,r.blockUntil=void 0),0!==r.timestamps.length||r.blocked||this.requests.delete(i)}static create(t,e){return new RateLimiter({maxRequests:t,windowMs:e,skipSuccessfulRequests:!1,skipFailedRequests:!0})}static forMessages(t=10){return new RateLimiter({maxRequests:t,windowMs:6e4,skipSuccessfulRequests:!1,skipFailedRequests:!0})}static forAPI(t=5){return new RateLimiter({maxRequests:t,windowMs:1e3,skipSuccessfulRequests:!1,skipFailedRequests:!1})}}class CacheManager{constructor(t={}){this.cache=new Map,this.stats={hits:0,misses:0,evictions:0},this.options={defaultTTL:t.defaultTTL||3e5,maxSize:t.maxSize||1e3,cleanupInterval:t.cleanupInterval||6e4,onEvict:t.onEvict||(()=>{})},this.startCleanup()}set(t,e,i){const r=Date.now(),n=i||this.options.defaultTTL;this.cache.size>=this.options.maxSize&&!this.cache.has(t)&&this.evictLRU();const s={value:e,timestamp:r,ttl:n,accessCount:0,lastAccessed:r};this.cache.set(t,s)}get(t){const e=this.cache.get(t);if(!e)return void this.stats.misses++;const i=Date.now();return i-e.timestamp>e.ttl?(this.delete(t),void this.stats.misses++):(e.accessCount++,e.lastAccessed=i,this.stats.hits++,e.value)}has(t){const e=this.cache.get(t);if(!e)return!1;return!(Date.now()-e.timestamp>e.ttl)||(this.delete(t),!1)}delete(t){const e=this.cache.get(t);return!!e&&(this.options.onEvict(t,e.value),this.cache.delete(t))}clear(){for(const[t,e]of this.cache.entries())this.options.onEvict(t,e.value);this.cache.clear(),this.resetStats()}async getOrSet(t,e,i){const r=this.get(t);if(void 0!==r)return r;const n=await e();return this.set(t,n,i),n}touch(t,e){const i=this.cache.get(t);if(!i)return!1;const r=Date.now();return i.timestamp=r,i.lastAccessed=r,void 0!==e&&(i.ttl=e),!0}getInfo(t){const e=this.cache.get(t);if(e)return{timestamp:e.timestamp,ttl:e.ttl,accessCount:e.accessCount,lastAccessed:e.lastAccessed}}keys(){return Array.from(this.cache.keys())}values(){return Array.from(this.cache.values()).map(t=>t.value)}size(){return this.cache.size}getStats(){const t=this.stats.hits+this.stats.misses,e=t>0?this.stats.hits/t:0;return{size:this.cache.size,maxSize:this.options.maxSize,hits:this.stats.hits,misses:this.stats.misses,hitRate:Math.round(1e4*e)/100,evictions:this.stats.evictions}}resetStats(){this.stats={hits:0,misses:0,evictions:0}}cleanup(){const t=Date.now();let e=0;for(const[i,r]of this.cache.entries())t-r.timestamp>r.ttl&&(this.delete(i),e++);return e}destroy(){this.cleanupTimer&&(clearInterval(this.cleanupTimer),this.cleanupTimer=void 0),this.clear()}startCleanup(){this.cleanupTimer=setInterval(()=>{this.cleanup()},this.options.cleanupInterval)}evictLRU(){let t,e=Date.now();for(const[i,r]of this.cache.entries())r.lastAccessed<e&&(e=r.lastAccessed,t=i);t&&(this.delete(t),this.stats.evictions++)}static create(t={}){return new CacheManager(t)}static forUsers(t=1e3){return new CacheManager({defaultTTL:6e5,maxSize:t,cleanupInterval:12e4})}static forMessages(t=5e3){return new CacheManager({defaultTTL:18e5,maxSize:t,cleanupInterval:3e5})}static forAPI(t=500){return new CacheManager({defaultTTL:6e4,maxSize:t,cleanupInterval:3e4})}}class ZaloPersonalClient extends t.EventEmitter{constructor(t,e){super(),this._isConnected=!1,this._isInitialized=!1,this.config=t,this.logger=e||Logger.create({prefix:"[PersonalClient]"}),this.rateLimiter=new RateLimiter(t.rateLimits||{maxRequests:5,windowMs:6e4}),this.cache=CacheManager.forUsers(1e3),this.auth=new PersonalAuth(this,this.logger.child("[Auth]")),this.messaging=new PersonalMessaging(this,this.logger.child("[Messaging]")),this.contacts=new PersonalContacts(this,this.logger.child("[Contacts]")),this.groups=new PersonalGroups(this,this.logger.child("[Groups]")),this.logger.info("ZaloPersonalClient đã được khởi tạo")}async initialize(){if(this._isInitialized)this.logger.warn("Client đã được khởi tạo rồi, bỏ qua việc khởi tạo lại");else try{this.logger.info("🔧 Đang khởi tạo Zalo Personal Client..."),this.zaloInstance=new e.Zalo,this.logger.info("✅ Đã tạo Zalo instance từ ZCA-JS"),this.setupEventListeners(),this.logger.info("✅ Đã thiết lập event listeners"),this._isInitialized=!0,this.logger.info("✅ Zalo Personal Client đã khởi tạo thành công và sẵn sàng sử dụng"),this.emit("initialized")}catch(t){throw this.logger.error("❌ Lỗi khởi tạo Zalo Personal Client:",t),t}}async performLogin(){return this.login()}async login(){if(!this.zaloInstance)throw new AuthenticationError("Zalo instance chưa được khởi tạo - vui lòng gọi initialize() trước");try{if(this.logger.info("🔐 Bắt đầu quá trình đăng nhập Zalo..."),this.config.credentials)this.logger.info("🔑 Đăng nhập bằng credentials đã lưu..."),this.api=await this.zaloInstance.login(this.config.credentials),this.logger.info("✅ Đăng nhập credentials thành công");else{if(!this.config.qrLogin)throw new AuthenticationError("Không có thông tin đăng nhập - cần cung cấp credentials hoặc qrLogin config");this.logger.info("📱 Đăng nhập bằng QR code..."),this.api=await this.zaloInstance.loginQR(this.config.qrLogin,t=>{this.logger.info("📡 QR Login Event nhận được:",t.type),this.emit("qr_event",t)}),this.logger.info("✅ Đăng nhập QR code thành công")}this._isConnected=!0,this.logger.info("🎉 Đăng nhập Zalo thành công, client đã sẵn sàng sử dụng"),this.emit("connected")}catch(t){if(this._isConnected=!1,this.logger.error("❌ Lỗi đăng nhập Zalo:",t),t.message?.includes("login"))throw AuthenticationError.loginFailed(t.message);throw new AuthenticationError("Đăng nhập thất bại",{cause:t})}}async disconnect(){try{this.logger.info("Đang đóng kết nối Zalo Personal Client..."),this.api?.listener&&this.api.listener.stop(),this.rateLimiter.destroy(),this.cache.destroy(),this._isConnected=!1,this._isInitialized=!1,this.logger.info("Đã đóng kết nối Zalo Personal Client"),this.emit("disconnected")}catch(t){throw this.logger.error("Lỗi đóng kết nối:",t),t}}isConnected(){return this._isConnected}isInitialized(){return this._isInitialized}getAPI(){if(!this.api)throw new AuthenticationError("Chưa đăng nhập");return this.api}getZaloInstance(){return this.zaloInstance}getRateLimiter(){return this.rateLimiter}getCache(){return this.cache}getLogger(){return this.logger}async apiCall(t,e){await this.rateLimiter.checkLimit(e);try{const i=await t();return this.rateLimiter.recordSuccess(e),i}catch(t){throw this.rateLimiter.recordFailure(e),t}}async keepAlive(){if(!this.api)throw new AuthenticationError("Chưa đăng nhập");try{await this.api.getUserInfo(),this.logger.debug("Keep alive successful")}catch(t){throw this.logger.error("Keep alive failed:",t),(t.message?.includes("auth")||t.message?.includes("login"))&&(this._isConnected=!1,this.emit("disconnected")),new NetworkError("Keep alive thất bại",{cause:t})}}setupEventListeners(){this.api?.listener?(this.api.listener.on("message",t=>{try{const e=this.transformMessage(t);this.logger.debug("Nhận tin nhắn:",e.id),this.emit("message",e)}catch(t){this.logger.error("Lỗi xử lý tin nhắn:",t)}}),this.api.listener.on("typing",t=>{this.logger.debug("Typing event:",t),this.emit("typing",t)}),this.api.listener.on("seen",t=>{this.logger.debug("Seen event:",t),this.emit("seen",t)}),this.api.listener.on("reaction",t=>{this.logger.debug("Reaction event:",t),this.emit("reaction",t)}),this.api.listener.start(),this.logger.info("Event listeners đã được thiết lập")):this.logger.warn("API listener không khả dụng")}transformMessage(t){return{id:t.data.msgId||t.data.cliMsgId,threadId:t.threadId,content:t.data.content,type:this.getMessageType(t.data.msgType),sender:{id:t.data.uidFrom,name:t.data.dName||"Unknown",avatar:t.data.avatar},timestamp:parseInt(t.data.ts)||Date.now(),isSelf:t.isSelf,quote:t.data.quote?this.transformQuote(t.data.quote):void 0,mentions:t.data.mentions||[]}}transformQuote(t){return{id:t.globalMsgId?.toString()||t.cliMsgId?.toString(),threadId:"",content:t.msg,type:"text",sender:{id:t.ownerId?.toString()||"",name:t.fromD||"Unknown"},timestamp:t.ts||Date.now(),isSelf:!1}}getMessageType(t){switch(t){case"webchat":case"chat.text":default:return"text";case"chat.photo":return"image";case"chat.video":return"video";case"chat.sticker":return"sticker";case"chat.file":return"file"}}}class OfficialAuth{constructor(t,e){this.client=t,this.logger=e}async getAccessToken(){try{this.logger.info("Đang lấy access token...");const t=this.client.getHttpClient(),e=(await t.post("/v2.0/oa/access_token",{app_id:this.getAppId(),app_secret:this.getAppSecret()})).data.access_token;if(!e)throw new Error("Không nhận được access token");return this.logger.info("Lấy access token thành công"),e}catch(t){throw this.logger.error("Lỗi lấy access token:",t),AuthenticationError.loginFailed(t.message)}}async refreshAccessToken(t){try{this.logger.info("Đang refresh access token...");const e=this.client.getHttpClient(),i=(await e.post("/v2.0/oa/access_token",{app_id:this.getAppId(),app_secret:this.getAppSecret(),refresh_token:t,grant_type:"refresh_token"})).data.access_token;if(!i)throw new Error("Không nhận được access token mới");return this.client.setAccessToken(i),this.logger.info("Refresh access token thành công"),i}catch(t){throw this.logger.error("Lỗi refresh access token:",t),AuthenticationError.tokenExpired()}}async getProfile(){try{const t=this.client.getHttpClient(),e=await t.get("/v2.0/oa/getprofile");return this.logger.info("Lấy profile OA thành công"),e.data.data}catch(t){throw this.logger.error("Lỗi lấy profile OA:",t),AuthenticationError.accessDenied("profile")}}getAppId(){const t=this.client.config;if(!t.appId)throw AuthenticationError.invalidCredentials();return t.appId}getAppSecret(){const t=this.client.config;if(!t.appSecret)throw AuthenticationError.invalidCredentials();return t.appSecret}}class OfficialMessaging{constructor(t,e){this.client=t,this.logger=e}async sendText(t,e){return this.validateUserId(t),this.validateTextContent(e),this.client.apiCall(async()=>{try{const i=this.client.getHttpClient(),r=await i.post("/v2.0/oa/message",{recipient:{user_id:t},message:{text:e}});return this.logger.info(`Gửi tin nhắn text thành công: ${t}`),{messageId:r.data.data.message_id,timestamp:Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi tin nhắn text:",t),new NetworkError("Gửi tin nhắn thất bại",{cause:t})}},`message_${t}`)}async sendTemplate(t,e){return this.validateUserId(t),this.validateTemplate(e),this.client.apiCall(async()=>{try{const i=this.client.getHttpClient(),r=await i.post("/v2.0/oa/message",{recipient:{user_id:t},message:{attachment:{type:"template",payload:{template_type:e.templateId,elements:[e.templateData]}}}});return this.logger.info(`Gửi template message thành công: ${t}`),{messageId:r.data.data.message_id,timestamp:Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi template message:",t),new NetworkError("Gửi template message thất bại",{cause:t})}},`template_${t}`)}async sendButtons(t,e,i){return this.validateUserId(t),this.validateTextContent(e),this.validateButtons(i),this.client.apiCall(async()=>{try{const r=this.client.getHttpClient(),n=await r.post("/v2.0/oa/message",{recipient:{user_id:t},message:{attachment:{type:"template",payload:{template_type:"button",text:e,buttons:i.map(t=>({type:t.type,title:t.title,payload:t.payload,url:t.url}))}}}});return this.logger.info(`Gửi button message thành công: ${t}`),{messageId:n.data.data.message_id,timestamp:Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi button message:",t),new NetworkError("Gửi button message thất bại",{cause:t})}},`buttons_${t}`)}async sendImage(t,e){return this.validateUserId(t),this.validateUrl(e),this.client.apiCall(async()=>{try{const i=this.client.getHttpClient(),r=await i.post("/v2.0/oa/message",{recipient:{user_id:t},message:{attachment:{type:"image",payload:{url:e}}}});return this.logger.info(`Gửi hình ảnh thành công: ${t}`),{messageId:r.data.data.message_id,timestamp:Date.now(),success:!0}}catch(t){throw this.logger.error("Lỗi gửi hình ảnh:",t),new NetworkError("Gửi hình ảnh thất bại",{cause:t})}},`image_${t}`)}async broadcast(t,e){if(!t||0===t.length)throw ValidationError.required("userIds");if(t.length>100)throw ValidationError.invalidValue("userIds",t,["Tối đa 100 users"]);this.validateTextContent(e);const i=[];for(const r of t)try{const t=await this.sendText(r,e);i.push(t)}catch(t){this.logger.error(`Lỗi broadcast đến ${r}:`,t),i.push({messageId:"",timestamp:Date.now(),success:!1})}return this.logger.info(`Broadcast hoàn thành: ${i.filter(t=>t.success).length}/${t.length} thành công`),i}validateUserId(t){if(!t)throw ValidationError.required("userId");if("string"!=typeof t)throw ValidationError.invalidType("userId",t,"string")}validateTextContent(t){if(!t)throw ValidationError.required("content");if("string"!=typeof t)throw ValidationError.invalidType("content",t,"string");if(t.length>2e3)throw ValidationError.invalidLength("content",t,void 0,2e3)}validateTemplate(t){if(!t.templateId)throw ValidationError.required("templateId");if(!t.templateData)throw ValidationError.required("templateData")}validateButtons(t){if(!t||0===t.length)throw ValidationError.required("buttons");if(t.length>3)throw ValidationError.invalidValue("buttons",t,["Tối đa 3 buttons"]);for(const e of t)if(!e.type||!e.title)throw ValidationError.required("button.type và button.title")}validateUrl(t){if(!t)throw ValidationError.required("url");try{new URL(t)}catch{throw ValidationError.invalidUrl(t)}}}class OfficialUsers{constructor(t,e){this.client=t,this.logger=e}async getUser(t){if(!t)throw ValidationError.required("userId");return this.client.apiCall(async()=>{try{const e=this.client.getHttpClient(),i=await e.get(`/v2.0/oa/getprofile?data={"user_id":"${t}"}`);return this.logger.info(`Lấy thông tin user: ${t}`),this.transformUser(i.data.data)}catch(t){throw this.logger.error("Lỗi lấy thông tin user:",t),new NetworkError("Lấy thông tin user thất bại",{cause:t})}},`get_user_${t}`)}async getFollowers(t=0,e=50){return this.client.apiCall(async()=>{try{const i=this.client.getHttpClient(),r=(await i.get(`/v2.0/oa/getfollowers?data={"offset":${t},"count":${e}}`)).data.data;return this.logger.info(`Lấy danh sách followers: ${r.followers.length}/${r.total}`),{users:r.followers.map(t=>this.transformUser(t)),total:r.total}}catch(t){throw this.logger.error("Lỗi lấy danh sách followers:",t),new NetworkError("Lấy danh sách followers thất bại",{cause:t})}},"get_followers")}async tagUser(t,e){if(!t)throw ValidationError.required("userId");if(!e)throw ValidationError.required("tagName");return this.client.apiCall(async()=>{try{const i=this.client.getHttpClient();return await i.post("/v2.0/oa/tag/tagfollower",{user_id:t,tag_name:e}),this.logger.info(`Tag user thành công: ${t} -> ${e}`),!0}catch(t){throw this.logger.error("Lỗi tag user:",t),new NetworkError("Tag user thất bại",{caus