diff --git a/MULTI_SESSION_USAGE.md b/MULTI_SESSION_USAGE.md
new file mode 100644
index 0000000000..74b4c086fc
--- /dev/null
+++ b/MULTI_SESSION_USAGE.md
@@ -0,0 +1,800 @@
+# Open-WA 多Session模式使用指南
+
+## 概述
+
+多Session模式允许你在同一个服务器实例上同时运行多个WhatsApp会话,每个会话都是独立的,可以连接不同的WhatsApp账号。
+
+## 快速开始
+
+### 1. 构建项目
+
+```bash
+npm run build
+```
+
+### 2. 启动多Session服务器
+
+```bash
+# 使用默认配置启动
+node bin/multi-session.js
+
+# 或指定端口和最大session数
+node bin/multi-session.js --port 3000 --max-sessions 5 --use-chrome
+```
+
+### 3. 服务器信息
+
+启动后,服务器将在以下端点可用:
+
+- **服务器信息**: `GET http://localhost:8080/info`
+- **健康检查**: `GET http://localhost:8080/health`
+- **Session管理**: `http://localhost:8080/api/v1/sessions`
+
+## API接口
+
+### Session管理
+
+#### 创建新Session并获取QR码
+
+```bash
+# 创建session并等待QR码生成
+curl -X POST http://localhost:8080/api/v1/sessions \
+ -H "Content-Type: application/json" \
+ -d '{
+ "sessionId": "my-session-1",
+ "waitForQR": true,
+ "config": {
+ "multiDevice": true,
+ "headless": true
+ }
+ }'
+```
+
+响应示例:
+```json
+{
+ "success": true,
+ "message": "Session created successfully",
+ "data": {
+ "sessionId": "my-session-1",
+ "status": "qr_ready",
+ "createdAt": "2024-01-16T10:30:00.000Z",
+ "lastActivity": "2024-01-16T10:30:05.000Z",
+ "qrCode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
+ "hasQRCode": true,
+ "qrCodeUrl": "/api/v1/sessions/my-session-1/qr.png"
+ }
+}
+```
+
+#### 获取QR码的多种方式
+
+**方式1: 获取QR码JSON数据**
+```bash
+# 获取base64格式的QR码
+curl http://localhost:8080/api/v1/sessions/my-session-1/qr
+
+# 获取QR码URL引用
+curl http://localhost:8080/api/v1/sessions/my-session-1/qr?format=url
+
+# 等待QR码生成(如果还未生成)
+curl http://localhost:8080/api/v1/sessions/my-session-1/qr?wait=true
+```
+
+**方式2: 直接获取QR码图片**
+```bash
+# 直接下载QR码图片
+curl http://localhost:8080/api/v1/sessions/my-session-1/qr.png -o qr-code.png
+
+# 在浏览器中直接查看QR码
+# 访问: http://localhost:8080/api/v1/sessions/my-session-1/qr.png
+```
+
+#### 获取所有Sessions
+
+```bash
+curl http://localhost:8080/api/v1/sessions
+```
+
+#### 获取特定Session信息
+
+```bash
+curl http://localhost:8080/api/v1/sessions/my-session-1
+```
+
+#### 获取Session状态
+
+```bash
+curl http://localhost:8080/api/v1/sessions/my-session-1/status
+```
+
+#### 删除Session
+
+```bash
+curl -X DELETE http://localhost:8080/api/v1/sessions/my-session-1
+```
+
+### 消息发送
+
+#### 发送文本消息
+
+```bash
+curl -X POST http://localhost:8080/api/v1/sessions/my-session-1/send-message \
+ -H "Content-Type: application/json" \
+ -d '{
+ "to": "1234567890@c.us",
+ "message": "Hello from Open-WA!"
+ }'
+```
+
+## 前端集成示例
+
+### HTML + JavaScript 示例
+
+```html
+
+
+
+ WhatsApp Multi-Session QR Scanner
+
+
+
+ WhatsApp Multi-Session Manager
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### React 组件示例
+
+```jsx
+import React, { useState, useEffect } from 'react';
+
+const WhatsAppSessionManager = () => {
+ const [sessions, setSessions] = useState({});
+ const [sessionId, setSessionId] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ const API_BASE = 'http://localhost:8080/api/v1';
+
+ useEffect(() => {
+ loadSessions();
+ }, []);
+
+ const loadSessions = async () => {
+ try {
+ const response = await fetch(`${API_BASE}/sessions`);
+ const result = await response.json();
+ if (result.success) {
+ setSessions(result.data);
+ }
+ } catch (error) {
+ console.error('加载sessions失败:', error);
+ }
+ };
+
+ const createSession = async () => {
+ if (!sessionId.trim()) {
+ alert('请输入Session ID');
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const response = await fetch(`${API_BASE}/sessions`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ sessionId: sessionId.trim(),
+ waitForQR: true,
+ config: {
+ multiDevice: true,
+ headless: true
+ }
+ })
+ });
+
+ const result = await response.json();
+ if (result.success) {
+ setSessions(prev => ({
+ ...prev,
+ [sessionId]: result.data
+ }));
+ setSessionId('');
+
+ // 开始监控session状态
+ monitorSession(sessionId);
+ } else {
+ alert('创建session失败: ' + result.error);
+ }
+ } catch (error) {
+ alert('创建session出错: ' + error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const monitorSession = (sessionId) => {
+ const interval = setInterval(async () => {
+ try {
+ const response = await fetch(`${API_BASE}/sessions/${sessionId}/status`);
+ const result = await response.json();
+
+ if (result.success) {
+ setSessions(prev => ({
+ ...prev,
+ [sessionId]: {
+ ...prev[sessionId],
+ status: result.data.status,
+ lastActivity: result.data.lastActivity
+ }
+ }));
+
+ // 如果已认证,停止监控
+ if (result.data.status === 'authenticated' || result.data.status === 'ready') {
+ clearInterval(interval);
+ }
+ }
+ } catch (error) {
+ console.error('检查session状态失败:', error);
+ }
+ }, 3000);
+
+ return interval;
+ };
+
+ const deleteSession = async (sessionId) => {
+ try {
+ const response = await fetch(`${API_BASE}/sessions/${sessionId}`, {
+ method: 'DELETE'
+ });
+
+ const result = await response.json();
+ if (result.success) {
+ setSessions(prev => {
+ const newSessions = { ...prev };
+ delete newSessions[sessionId];
+ return newSessions;
+ });
+ } else {
+ alert('删除session失败: ' + result.error);
+ }
+ } catch (error) {
+ alert('删除session出错: ' + error.message);
+ }
+ };
+
+ const getStatusColor = (status) => {
+ switch (status) {
+ case 'qr_ready': return '#ff9800';
+ case 'authenticated': case 'ready': return '#4caf50';
+ case 'failed': return '#f44336';
+ default: return '#757575';
+ }
+ };
+
+ return (
+
+
WhatsApp Multi-Session Manager
+
+
+ setSessionId(e.target.value)}
+ placeholder="Session ID"
+ style={{ marginRight: '10px', padding: '8px' }}
+ />
+
+
+
+
+ {Object.entries(sessions).map(([id, session]) => (
+
+
Session: {id}
+
+ 状态: {session.status}
+
+
创建时间: {new Date(session.createdAt).toLocaleString()}
+
+ {session.hasQRCode && session.status === 'qr_ready' && (
+
+
扫描二维码登录:
+

+
+
用WhatsApp扫描上方二维码
+
+ )}
+
+ {(session.status === 'authenticated' || session.status === 'ready') && (
+
+ ✅ 已成功登录!
+
+ )}
+
+
+
+ ))}
+
+
+ );
+};
+
+export default WhatsAppSessionManager;
+```
+
+## 部署指南
+
+### 1. 服务器部署
+
+#### 使用PM2部署
+
+```bash
+# 安装PM2
+npm install -g pm2
+
+# 创建ecosystem文件
+cat > ecosystem.config.js << EOF
+module.exports = {
+ apps: [{
+ name: 'open-wa-multi-session',
+ script: 'bin/multi-session.js',
+ args: '--port 8080 --max-sessions 10',
+ instances: 1,
+ autorestart: true,
+ watch: false,
+ max_memory_restart: '1G',
+ env: {
+ NODE_ENV: 'production',
+ PORT: 8080,
+ MAX_SESSIONS: 10,
+ HEADLESS: true,
+ USE_CHROME: false
+ }
+ }]
+}
+EOF
+
+# 启动应用
+pm2 start ecosystem.config.js
+pm2 save
+pm2 startup
+```
+
+#### 使用Docker部署
+
+```dockerfile
+# Dockerfile
+FROM node:18-alpine
+
+# 安装Chrome依赖
+RUN apk add --no-cache \
+ chromium \
+ nss \
+ freetype \
+ freetype-dev \
+ harfbuzz \
+ ca-certificates \
+ ttf-freefont
+
+# 设置Chrome路径
+ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
+ PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
+
+WORKDIR /app
+
+COPY package*.json ./
+RUN npm ci --only=production
+
+COPY . .
+RUN npm run build
+
+EXPOSE 8080
+
+CMD ["node", "bin/multi-session.js"]
+```
+
+```bash
+# 构建和运行
+docker build -t open-wa-multi-session .
+docker run -d -p 8080:8080 --name open-wa-ms open-wa-multi-session
+```
+
+### 2. 环境变量配置
+
+创建 `.env` 文件:
+
+```env
+# 服务器配置
+PORT=8080
+HOST=0.0.0.0
+MAX_SESSIONS=10
+
+# WhatsApp配置
+MULTI_DEVICE=true
+HEADLESS=true
+USE_CHROME=false
+
+# 功能配置
+ENABLE_CORS=true
+DISABLE_SPINS=true
+BLOCK_CRASH_LOGS=true
+
+# 会话配置
+SESSION_DATA_PATH=./sessions
+QR_TIMEOUT=120
+```
+
+## 使用示例
+
+### Node.js客户端示例
+
+```javascript
+const axios = require('axios');
+
+class OpenWAMultiSessionClient {
+ constructor(baseURL = 'http://localhost:8080') {
+ this.baseURL = baseURL;
+ this.api = axios.create({ baseURL: `${baseURL}/api/v1` });
+ }
+
+ async createSession(sessionId, config = {}) {
+ const response = await this.api.post('/sessions', {
+ sessionId,
+ config: { multiDevice: true, ...config }
+ });
+ return response.data;
+ }
+
+ async getSessionStatus(sessionId) {
+ const response = await this.api.get(`/sessions/${sessionId}/status`);
+ return response.data;
+ }
+
+ async sendMessage(sessionId, to, message) {
+ const response = await this.api.post(`/sessions/${sessionId}/send-message`, {
+ to,
+ message
+ });
+ return response.data;
+ }
+
+ async deleteSession(sessionId) {
+ const response = await this.api.delete(`/sessions/${sessionId}`);
+ return response.data;
+ }
+}
+
+// 使用示例
+async function main() {
+ const client = new OpenWAMultiSessionClient();
+
+ try {
+ // 创建session
+ await client.createSession('user1', { headless: true });
+ console.log('Session created');
+
+ // 等待session准备就绪
+ await new Promise(resolve => setTimeout(resolve, 10000));
+
+ // 发送消息
+ await client.sendMessage('user1', '1234567890@c.us', 'Hello!');
+ console.log('Message sent');
+
+ } catch (error) {
+ console.error('Error:', error.response?.data || error.message);
+ }
+}
+
+main();
+```
+
+### Python客户端示例
+
+```python
+import requests
+import time
+
+class OpenWAMultiSessionClient:
+ def __init__(self, base_url="http://localhost:8080"):
+ self.base_url = base_url
+ self.api_url = f"{base_url}/api/v1"
+
+ def create_session(self, session_id, config=None):
+ if config is None:
+ config = {}
+
+ payload = {
+ "sessionId": session_id,
+ "config": {"multiDevice": True, **config}
+ }
+
+ response = requests.post(f"{self.api_url}/sessions", json=payload)
+ response.raise_for_status()
+ return response.json()
+
+ def get_session_status(self, session_id):
+ response = requests.get(f"{self.api_url}/sessions/{session_id}/status")
+ response.raise_for_status()
+ return response.json()
+
+ def send_message(self, session_id, to, message):
+ payload = {"to": to, "message": message}
+ response = requests.post(
+ f"{self.api_url}/sessions/{session_id}/send-message",
+ json=payload
+ )
+ response.raise_for_status()
+ return response.json()
+
+# 使用示例
+client = OpenWAMultiSessionClient()
+
+# 创建session
+client.create_session("user1", {"headless": True})
+print("Session created")
+
+# 等待session准备
+time.sleep(10)
+
+# 发送消息
+client.send_message("user1", "1234567890@c.us", "Hello from Python!")
+print("Message sent")
+```
+
+## 注意事项
+
+### 1. 系统要求
+
+- **内存**: 每个session大约需要200-500MB内存
+- **CPU**: 多核CPU推荐,每个session会占用一定CPU资源
+- **存储**: 每个session会创建独立的数据目录
+
+### 2. 限制和建议
+
+- **最大Session数**: 建议不超过10个(取决于服务器性能)
+- **QR码扫描**: 每个session需要独立扫描QR码登录
+- **账号限制**: 每个WhatsApp账号只能同时在一个session中使用
+- **稳定性**: 建议使用headless模式以提高稳定性
+
+### 3. 故障排除
+
+#### Session创建失败
+```bash
+# 检查服务器日志
+pm2 logs open-wa-multi-session
+
+# 检查session状态
+curl http://localhost:8080/api/v1/sessions
+```
+
+#### 内存不足
+```bash
+# 监控资源使用
+pm2 monit
+
+# 调整最大session数
+export MAX_SESSIONS=5
+```
+
+#### 端口冲突
+```bash
+# 使用不同端口启动
+node bin/multi-session.js --port 3001
+```
+
+## 扩展功能
+
+你可以通过以下方式扩展多session功能:
+
+1. **添加新的API端点** - 在 `src/api/MultiSessionAPI.ts` 中添加
+2. **自定义Session配置** - 修改 `src/config/multiSessionConfig.ts`
+3. **添加中间件** - 在 `src/cli/multiSessionRoutes.ts` 中扩展路由
+
+## 技术支持
+
+如有问题,请参考:
+
+- [Open-WA文档](https://docs.openwa.dev)
+- [GitHub仓库](https://github.com/open-wa/wa-automate-nodejs)
+- [问题反馈](https://github.com/open-wa/wa-automate-nodejs/issues)
\ No newline at end of file
diff --git a/SESSION_RESTART_GUIDE.md b/SESSION_RESTART_GUIDE.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/bin/multi-session.js b/bin/multi-session.js
new file mode 100644
index 0000000000..39c666a7f3
--- /dev/null
+++ b/bin/multi-session.js
@@ -0,0 +1,132 @@
+#!/usr/bin/env node
+
+/**
+ * Open-WA Multi-Session Server
+ * Supports running multiple WhatsApp sessions simultaneously
+ */
+const express = require('express');
+const http = require('http');
+const path = require('path');
+const { log } = require('../dist/logging/logging');
+const { version } = require('../package.json');
+const { MultiSessionAPI } = require('../dist/api/MultiSessionAPI');
+const { initMultiSessionChatwoot, multiSessionChatwoot } = require('../dist/cli/integrations/chatwoot');
+const { SessionManager, globalSessionManager } = require('../dist/controllers/SessionManager');
+
+// Import the shared express app and its setup functions
+const { app, setUpExpressApp, setupMediaMiddleware } = require('../dist/cli/server');
+
+// This block is preserved as it correctly handles command-line help and version flags.
+const args = process.argv.slice(2);
+const helpFlags = ['-h', '--help'];
+const versionFlags = ['-v', '--version'];
+
+if (args.some(arg => helpFlags.includes(arg))) {
+ console.log(`
+🤖 Open-WA Multi-Session Server (Corrected Single-Server Architecture)
+
+Usage:
+ node bin/multi-session.js [options]
+
+Options:
+ --port Server port (default: 8081)
+ --host Server host (default: 0.0.0.0)
+ --webhook-host Publicly accessible host/IP for Chatwoot webhooks (default: localhost)
+ -h, --help Show this help
+ -v, --version Show version
+ `);
+ process.exit(0);
+}
+
+if (args.some(arg => versionFlags.includes(arg))) {
+ console.log(`Open-WA Multi-Session Server v${version}`);
+ process.exit(0);
+}
+
+// This function correctly parses arguments and is preserved.
+const parseArgs = () => {
+ const config = {};
+ for (let i = 0; i < args.length; i++) {
+ const arg = args[i];
+ const nextArg = args[i + 1];
+ switch (arg) {
+ case '--port':
+ config.port = parseInt(nextArg);
+ i++;
+ break;
+ case '--webhook-host':
+ config.webhookHost = nextArg;
+ i++;
+ break;
+ case '--host':
+ config.host = nextArg;
+ i++;
+ break;
+ }
+ }
+ return config;
+};
+
+// Main startup function, now unified into a single server architecture.
+async function start() {
+ try {
+ console.log('🚀 Starting Open-WA Multi-Session Server (Unified Architecture)...');
+ const cliConfig = parseArgs();
+
+ // 1. Setup the Express app, which correctly includes JSON parsing.
+ setUpExpressApp();
+
+ const port = cliConfig.port || 8081;
+ const host = cliConfig.host || '0.0.0.0';
+ const webhookHost = cliConfig.webhookHost || 'localhost';
+
+ // 2. Initialize the global session manager. This is a critical step.
+ const manager = new SessionManager({});
+ Object.assign(globalSessionManager, manager);
+ log.info('Global Session Manager Initialized.');
+
+ // 3. Initialize Chatwoot integration service.
+ initMultiSessionChatwoot({
+ webhookBaseUrl: `http://${webhookHost}:${port}/api/v1/sessions`
+ });
+ log.info('Chatwoot Integration Service Initialized.');
+
+ // 4. Setup the Multi-Session API routes on the single, unified app.
+ const multiSessionAPI = new MultiSessionAPI();
+ app.use('/api/v1', multiSessionAPI.getRouter());
+ log.info('Multi-Session API routes configured.');
+
+ // 5. Setup media handling middleware.
+ setupMediaMiddleware();
+ log.info('Media middleware configured.');
+
+ // 6. Add Web Management Interface with correct absolute paths
+ const webUIPath = path.join(__dirname, '../web-ui');
+ app.use('/assets', express.static(path.join(webUIPath, 'assets')));
+
+ app.get('/', (req, res) => {
+ res.sendFile(path.join(webUIPath, 'index.html'));
+ });
+ app.get('/dashboard', (req, res) => {
+ res.sendFile(path.join(webUIPath, 'index.html'));
+ });
+ log.info('Web Management Interface configured.');
+
+ // 7. Create and start the single, unified HTTP server.
+ const server = http.createServer(app);
+ server.listen(port, host, () => {
+ log.info(`✅ Server is unified and running at http://${host}:${port}`);
+ log.info(`🌐 Web Management Interface: http://${host}:${port}`);
+ log.info(`📡 API Base URL: http://${host}:${port}/api/v1`);
+ });
+
+ } catch (error) {
+ console.error('❌ FATAL: Failed to start Multi-Session Server:', error.message);
+ console.error(error.stack);
+ process.exit(1);
+ }
+}
+
+// Set a debug flag for more verbose logging, then start the server.
+process.env.DEBUG = 'true';
+start();
\ No newline at end of file
diff --git a/bin/oas-type-schemas.json b/bin/oas-type-schemas.json
index c1948c1b6a..86cc6c2d52 100644
--- a/bin/oas-type-schemas.json
+++ b/bin/oas-type-schemas.json
@@ -1 +1 @@
-{"ChatServer":{"title":"ChatServer","description":"The suffix used to identify a non-group chat id","enum":["c.us"],"type":"string"},"GroupChatServer":{"title":"GroupChatServer","description":"The suffix used to identify a group chat id","enum":["g.us"],"type":"string"},"WaServers":{"anyOf":[{"$ref":"#/components/schemas/ChatServer","title":"WaServers"},{"$ref":"#/components/schemas/GroupChatServer","title":"WaServers"}],"title":"WaServers","description":"A type alias for all available \"servers\""},"CountryCode":{"enum":[1,7,20,27,30,31,32,33,34,36,39,40,41,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58,60,61,62,63,64,65,66,81,82,84,86,90,91,92,93,94,95,98,211,212,213,216,218,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,248,249,250,251,252,253,254,255,256,257,258,260,261,262,263,264,265,266,267,268,269,290,291,297,298,299,350,351,352,353,354,355,356,357,358,359,370,371,372,373,374,375,376,377,378,380,381,382,383,385,386,387,389,420,421,423,500,501,502,503,504,505,506,507,508,509,590,591,592,593,594,595,596,597,598,599,670,672,673,674,675,676,677,678,679,680,681,682,683,685,686,687,688,689,690,691,692,850,852,853,855,856,880,886,960,961,962,963,964,965,966,967,968,970,971,972,973,974,975,976,977,992,993,994,995,996,998],"title":"CountryCode","description":"Type alias representing all available country codes","type":"number"},"AccountNumber":{"title":"AccountNumber","description":"The account number. It is made up of a country code and then the local number without the preceeding 0. For example, if a UK (+44) wa account is linked to the number 07123456789 then the account number will be 447123456789."},"GroupId":{"title":"GroupId","description":"A new group or community has the format of a random number followed by `@g.us`"},"GroupChatId":{"$ref":"#/components/schemas/GroupId","title":"GroupChatId","description":"A group chat ends with `@g.us` and usually has two parts, the timestamp of when it was created, and the user id of the number that created the group. For example `[creator number]-[timestamp]@g.us`\n\nExample:\n\n`\"447123456789-1445627445@g.us\"`"},"ContactId":{"title":"ContactId","description":"A contact id ends with `@c.us` and only contains the number of the contact. For example, if the country code of a contact is `44` and their number is `7123456789` then the contact id would be `447123456789@c.us`\n\nExample:\n\n`\"447123456789@c.us\"`"},"ChatId":{"anyOf":[{"$ref":"#/components/schemas/ContactId","title":"ChatId"},{"$ref":"#/components/schemas/GroupChatId","title":"ChatId"}],"title":"ChatId","description":"A chat id ends with `@c.us` or `@g.us` for group chats.\n\nExample:\n\nA group chat: `\"447123456789-1445627445@g.us\"`\nA group chat: `\"447123456789@g.us\"`"},"MessageId":{"title":"MessageId","description":"The id of a message. The format is `[boolean]_[ChatId]_[random character string]`\n\nExample:\n\n`\"false_447123456789@c.us_9C4D0965EA5C09D591334AB6BDB07FEB\"`"},"Content":{"title":"Content","description":"This is a generic type alias for the content of a message\n\nExample:\n\n`\"hello!\"`"},"NonSerializedId":{"properties":{"server":{"$ref":"#/components/schemas/WaServers","title":"NonSerializedId.server"},"user":{"$ref":"#/components/schemas/AccountNumber","title":"NonSerializedId.user"},"_serialized":{"$ref":"#/components/schemas/ContactId","title":"NonSerializedId._serialized"}},"required":["server","user","_serialized"],"additionalProperties":false,"title":"NonSerializedId","type":"object"},"DataURL":{"title":"DataURL","description":"Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents. They were formerly known as \"data URIs\" until that name was retired by the WHATWG.\n\n\nData URLs are composed of four parts: a prefix (data:), a MIME type indicating the type of data, an optional base64 token if non-textual, and the data itself:\n\nExample:\n`\"data:[][;base64],\"`\n\nLearn more here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs"},"Base64":{"title":"Base64","description":"Base64 is basically a file encoded as a string.\n\nBase64 is a group of similar binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation. The term Base64 originates from a specific MIME content transfer encoding.\n\nLearn more here: https://developer.mozilla.org/en-US/docs/Glossary/Base64"},"FilePath":{"title":"FilePath","description":"The relative or absolute path of a file\n\nLearn more here: https://www.w3schools.com/html/html_filepaths.asp"},"GetURL":{"title":"GetURL","description":"A URL of a file used with a GET request"},"AdvancedFile":{"anyOf":[{"$ref":"#/components/schemas/DataURL","title":"AdvancedFile"},{"$ref":"#/components/schemas/FilePath","title":"AdvancedFile"},{"$ref":"#/components/schemas/GetURL","title":"AdvancedFile"}],"title":"AdvancedFile","description":"Some file based actions in open-wa are powerful enough to take a dataurl, url or filepath"},"Button":{"properties":{"id":{"title":"Button.id","type":"string"},"text":{"title":"Button.text","type":"string"}},"required":["id","text"],"additionalProperties":false,"title":"Button","type":"object"},"AdvancedButton":{"properties":{"id":{"title":"AdvancedButton.id","type":"string"},"text":{"title":"AdvancedButton.text","type":"string"},"url":{"title":"AdvancedButton.url","type":"string"},"number":{"title":"AdvancedButton.number","type":"string"}},"required":["text"],"additionalProperties":false,"title":"AdvancedButton","type":"object"},"Row":{"properties":{"title":{"title":"Row.title","type":"string"},"description":{"title":"Row.description","type":"string"},"rowId":{"title":"Row.rowId","type":"string"}},"required":["title","description","rowId"],"additionalProperties":false,"title":"Row","type":"object"},"Section":{"properties":{"title":{"title":"Section.title","type":"string"},"rows":{"items":{"$ref":"#/components/schemas/Row","title":"Section.rows.[]"},"title":"Section.rows","type":"array"}},"required":["title","rows"],"additionalProperties":false,"title":"Section","type":"object"},"LocationButtonBody":{"properties":{"lat":{"title":"LocationButtonBody.lat","type":"number"},"lng":{"title":"LocationButtonBody.lng","type":"number"},"caption":{"title":"LocationButtonBody.caption","type":"string"}},"required":["lat","lng","caption"],"additionalProperties":false,"title":"LocationButtonBody","type":"object"},"Call":{"properties":{"id":{"title":"Call.id","description":"The id of the call","type":"string"},"peerJid":{"$ref":"#/components/schemas/ContactId","title":"Call.peerJid","description":"The id of the account calling"},"offerTime":{"title":"Call.offerTime","description":"The epoch timestamp of the call. You will have to multiply this by 1000 to get the actual epoch timestamp","type":"number"},"isVideo":{"title":"Call.isVideo","description":"Whether or not the call is a video call","type":"boolean"},"isGroup":{"title":"Call.isGroup","description":"Whether or not the call is a group call","type":"boolean"},"canHandleLocally":{"title":"Call.canHandleLocally","type":"boolean"},"outgoing":{"title":"Call.outgoing","description":"The direction of the call.","type":"boolean"},"webClientShouldHandle":{"title":"Call.webClientShouldHandle","type":"boolean"},"participants":{"items":{"$ref":"#/components/schemas/ContactId","title":"Call.participants.[]"},"title":"Call.participants","description":"The other participants on a group call","type":"array"},"State":{"$ref":"#/components/schemas/CallState","title":"Call.State","description":"State of the call"}},"required":["id","peerJid","offerTime","isVideo","isGroup","canHandleLocally","outgoing","webClientShouldHandle","participants","State"],"additionalProperties":false,"title":"Call","type":"object"},"BaseChat":{"properties":{"archive":{"title":"BaseChat.archive","type":"boolean"},"changeNumberNewJid":{"title":"BaseChat.changeNumberNewJid"},"changeNumberOldJid":{"title":"BaseChat.changeNumberOldJid"},"contact":{"$ref":"#/components/schemas/Contact","title":"BaseChat.contact","description":"The contact related to this chat"},"groupMetadata":{"$ref":"#/components/schemas/GroupMetadata","title":"BaseChat.groupMetadata","description":"Group metadata for this chat"},"isAnnounceGrpRestrict":{"title":"BaseChat.isAnnounceGrpRestrict","description":"If the chat is a group chat is restricted"},"formattedTitle":{"title":"BaseChat.formattedTitle","description":"The title of the chat","type":"string"},"canSend":{"title":"BaseChat.canSend","description":"Whether your host account is able to send messages to this chat","type":"boolean"},"isReadOnly":{"title":"BaseChat.isReadOnly","description":"Whether the chat is a group chat and the group is restricted","type":"boolean"},"kind":{"title":"BaseChat.kind","type":"string"},"labels":{"title":"BaseChat.labels","description":"The labels attached to this chat."},"lastReceivedKey":{"title":"BaseChat.lastReceivedKey","description":"The ID of the last message received in this chat"},"modifyTag":{"title":"BaseChat.modifyTag","type":"number"},"msgs":{"title":"BaseChat.msgs","description":"The messages in the chat"},"muteExpiration":{"title":"BaseChat.muteExpiration","description":"The expiration timestamp of the chat mute","type":"number"},"name":{"title":"BaseChat.name","description":"The name of the chat","type":"string"},"notSpam":{"title":"BaseChat.notSpam","description":"Whether the chat is marked as spam","type":"boolean"},"pendingMsgs":{"title":"BaseChat.pendingMsgs","description":"Messages that are pending to be sent","type":"boolean"},"pin":{"title":"BaseChat.pin","description":"Whether the chat is pinned","type":"number"},"presence":{"title":"BaseChat.presence","description":"The presence state of the chat participant"},"t":{"title":"BaseChat.t","description":"The timestamp of the last interaction in the chat","type":"number"},"unreadCount":{"title":"BaseChat.unreadCount","description":"The number of undread messages in this chat","type":"number"},"ack":{"title":"BaseChat.ack"},"isOnline":{"title":"BaseChat.isOnline","description":"@deprecated This is unreliable. Use the method [`isChatOnline`](https://open-wa.github.io/wa-automate-nodejs/classes/client.html#ischatonline) instead."},"lastSeen":{"title":"BaseChat.lastSeen","description":"@deprecated This is unreliable. Use the method [`getLastSeen`](https://open-wa.github.io/wa-automate-nodejs/classes/client.html#getlastseen) instead."},"pic":{"title":"BaseChat.pic","description":"URL of the chat picture if available","type":"string"}},"required":["archive","changeNumberNewJid","changeNumberOldJid","contact","groupMetadata","isAnnounceGrpRestrict","isReadOnly","kind","labels","lastReceivedKey","modifyTag","msgs","muteExpiration","name","notSpam","pendingMsgs","pin","presence","t","unreadCount"],"additionalProperties":false,"title":"BaseChat","type":"object"},"SingleChat":{"properties":{"id":{"$ref":"#/components/schemas/ContactId","title":"SingleChat.id","description":"The id of the chat"},"isGroup":{"title":"SingleChat.isGroup","description":"Whether the chat is a group chat","enum":[false],"type":"boolean"}},"required":["id","isGroup"],"additionalProperties":false,"title":"SingleChat","type":"object"},"GroupChat":{"properties":{"id":{"$ref":"#/components/schemas/GroupChatId","title":"GroupChat.id","description":"The id of the chat"},"isGroup":{"title":"GroupChat.isGroup","description":"Whether the chat is a group chat","enum":[true],"type":"boolean"},"groupType":{"enum":["DEFAULT","COMMUNITY","LINKED_ANNOUNCEMENT_GROUP","LINKED_GENERAL_GROUP","LINKED_SUBGROUP"],"title":"GroupChat.groupType","description":"The type of the group","type":"string"}},"required":["id","isGroup","groupType"],"additionalProperties":false,"title":"GroupChat","type":"object"},"Chat":{"anyOf":[{"$ref":"#/components/schemas/SingleChat","title":"Chat"},{"$ref":"#/components/schemas/GroupChat","title":"Chat"}],"title":"Chat"},"LiveLocationChangedEvent":{"properties":{"id":{"title":"LiveLocationChangedEvent.id","type":"string"},"lat":{"title":"LiveLocationChangedEvent.lat","type":"number"},"lng":{"title":"LiveLocationChangedEvent.lng","type":"number"},"speed":{"title":"LiveLocationChangedEvent.speed","type":"number"},"lastUpdated":{"title":"LiveLocationChangedEvent.lastUpdated","type":"number"},"accuracy":{"title":"LiveLocationChangedEvent.accuracy","type":"number"},"degrees":{"title":"LiveLocationChangedEvent.degrees"},"msgId":{"title":"LiveLocationChangedEvent.msgId","description":"The message id that was sent when the liveLocation session was started.","type":"string"}},"required":["id","lat","lng","speed","lastUpdated","accuracy","degrees"],"additionalProperties":false,"title":"LiveLocationChangedEvent","type":"object"},"GroupChatCreationParticipantAddResponse":{"properties":{"code":{"enum":[200,400,403],"title":"GroupChatCreationParticipantAddResponse.code","description":"The resultant status code for adding the participant.\n\n200 if the participant was added successfully during the creation of the group.\n\n403 if the participant does not allow their account to be added to group chats. If you receive a 403, you will also get an `invite_code` and `invite_code_exp`","type":"number"},"invite_code":{"title":"GroupChatCreationParticipantAddResponse.invite_code","description":"If the participant is not allowed to be added to group chats due to their privacy settings, you will receive an `invite_code` which you can send to them via a text.","type":"string"},"invite_code_exp":{"title":"GroupChatCreationParticipantAddResponse.invite_code_exp","description":"The expiry ts of the invite_code. It is a number wrapped in a string, in order to get the proper time you can use this:\n\n```javascript\n new Date(Number(invite_code_exp)*1000)\n```","type":"string"}},"required":["code"],"additionalProperties":false,"title":"GroupChatCreationParticipantAddResponse","type":"object"},"GroupChatCreationResponse":{"properties":{"status":{"enum":[200,400],"title":"GroupChatCreationResponse.status","description":"The resultant status code of the group chat creation.\n\n200 if the group was created successfully.\n\n400 if the initial participant does not exist","type":"number"},"gid":{"$ref":"#/components/schemas/GroupChatId","title":"GroupChatCreationResponse.gid","description":"The group chat id"},"participants":{"items":{"properties":{"ContactId":{"$ref":"#/components/schemas/GroupChatCreationParticipantAddResponse","title":"GroupChatCreationResponse.participants.[].ContactId"}},"additionalProperties":false,"title":"GroupChatCreationResponse.participants.[]","type":"object"},"title":"GroupChatCreationResponse.participants","description":"The initial requested participants and their corresponding add responses","type":"array"}},"required":["status","gid","participants"],"additionalProperties":false,"title":"GroupChatCreationResponse","type":"object"},"EphemeralDuration":{"enum":[86400,604800,7776000],"title":"EphemeralDuration","description":"Ephemeral duration can be 1 day, 7 days or 90 days. The default is 1 day.","type":"number"},"SessionData":{"properties":{"WABrowserId":{"title":"SessionData.WABrowserId","type":"string"},"WASecretBundle":{"title":"SessionData.WASecretBundle","type":"string"},"WAToken1":{"title":"SessionData.WAToken1","type":"string"},"WAToken2":{"title":"SessionData.WAToken2","type":"string"}},"additionalProperties":false,"title":"SessionData","type":"object"},"DevTools":{"properties":{"user":{"title":"DevTools.user","description":"Username for devtools","type":"string"},"pass":{"title":"DevTools.pass","description":"Password for devtools","type":"string"}},"required":["user","pass"],"additionalProperties":false,"title":"DevTools","type":"object"},"EventPayload":{"properties":{"ts":{"title":"EventPayload.ts","type":"number"},"sessionId":{"title":"EventPayload.sessionId","type":"string"},"id":{"title":"EventPayload.id","type":"string"},"event":{"$ref":"#/components/schemas/SimpleListener","title":"EventPayload.event"},"data":{"title":"EventPayload.data"}},"required":["ts","sessionId","id","event","data"],"additionalProperties":{},"title":"EventPayload","type":"object"},"Webhook":{"properties":{"url":{"title":"Webhook.url","description":"The endpoint to send (POST) the event to.","type":"string"},"requestConfig":{"$ref":"#/components/schemas/AxiosRequestConfig","title":"Webhook.requestConfig","description":"The optional AxiosRequestConfig to use for firing the webhook event. This can be useful if you want to add some authentication when POSTing data to your server.\n\nFor example, if your webhook requires the username `admin` and password `1234` for authentication, you can set the requestConfig to:\n```\n{\n auth: {\n username: \"admin\",\n password: \"1234\",\n }\n}\n```\n\nPlease note, for security reasons, this is not returned when listing webhooks however it is returned when registering a webhook for verification purposes."},"id":{"title":"Webhook.id","description":"The ID of the given webhook setup. Use this ID with [[removeWebhook]]","type":"string"},"events":{"items":{"$ref":"#/components/schemas/SimpleListener","title":"Webhook.events.[]"},"title":"Webhook.events","description":"An array of events that are registered to be sent to this webhook.","type":"array"},"ts":{"title":"Webhook.ts","description":"Time when the webhook was registered in epoch time","type":"number"}},"required":["url","id","events","ts"],"additionalProperties":false,"title":"Webhook","type":"object"},"ProxyServerCredentials":{"properties":{"protocol":{"title":"ProxyServerCredentials.protocol","description":"The protocol on which the proxy is running. E.g `http`, `https`, `socks4` or `socks5`. This is optional and can be automatically determined from the address.","type":"string"},"address":{"title":"ProxyServerCredentials.address","description":"Proxy Server address. This can include the port e.g '127.0.0.1:5005'","type":"string"},"username":{"title":"ProxyServerCredentials.username","description":"Username for Proxy Server authentication","type":"string"},"password":{"title":"ProxyServerCredentials.password","description":"Password for Proxy Server authentication","type":"string"}},"required":["address","username","password"],"additionalProperties":false,"title":"ProxyServerCredentials","type":"object"},"ConfigObject":{"properties":{"sessionData":{"anyOf":[{"$ref":"#/components/schemas/SessionData","title":"ConfigObject.sessionData"},{"$ref":"#/components/schemas/Base64","title":"ConfigObject.sessionData"}],"title":"ConfigObject.sessionData","description":"@deprecated The authentication object (as a JSON object or a base64 encoded string) that is required to migrate a session from one instance to another or to just restart an existing instance.\nThis sessionData is provided in a generated JSON file (it's a json file but contains the JSON data as a base64 encoded string) upon QR scan or an event.\n\nYou can capture the event like so:\n```javascript\nimport {create, ev} from '@open-wa/wa-automate';\n\n ev.on('sessionData.**', async (sessionData, sessionId) =>{\n console.log(sessionId, sessionData)\n })\n\n//or as base64 encoded string\n\n ev.on('sessionDataBase64.**', async (sessionDatastring, sessionId) =>{\n console.log(sessionId, sessionDatastring)\n })\n```\n NOTE: You can set sessionData as an evironmental variable also! The variable name has to be [sessionId (default = 'session) in all caps]_DATA_JSON. You have to make sure to surround your session data with single quotes to maintain the formatting.\n\nFor example:\n\nsessionId = 'session'\n\nTo set env var:\n```bash\n export SESSION_DATA_JSON=`...`\n```\nwhere ... is copied from session.data.json this will be a string most likley starting in `ey...` and ending with `==`\n\nSetting the sessionData in the environmental variable will override the sessionData object in the config."},"linkCode":{"title":"ConfigObject.linkCode","description":"There is a new way to login to your host account by entering a link code after a confirmation step from the host account device. In order to use this feature you MUST set the host account number as a string or number beforehand as a property of the config object.\n\ne.g\n```\nlinkCode: '1234567890'\n```","type":"string"},"browserWSEndpoint":{"title":"ConfigObject.browserWSEndpoint","description":"ALPHA EXPERIMENTAL FEATURE! DO NOT USE IN PRODUCTION, REQUIRES TESTING.\n\nLearn more:\n\nhttps://pptr.dev/#?product=Puppeteer&version=v3.1.0&show=api-puppeteerconnectoptions\n\nhttps://medium.com/@jaredpotter1/connecting-puppeteer-to-existing-chrome-window-8a10828149e0","type":"string"},"useStealth":{"title":"ConfigObject.useStealth","description":"This flag allows you to disable or enable the use of the puppeteer stealth plugin. It is a good idea to use it, however it can cause issues sometimes. Set this to false if you are experiencing `browser.setMaxListeneres` issue. For now the default for this is false.","default":"`false`","type":"boolean"},"sessionDataPath":{"title":"ConfigObject.sessionDataPath","description":"The path relative to the current working directory (i.e where you run the command to start your process). This will be used to store and read your `.data.json` files. defualt to ''","type":"string"},"bypassCSP":{"title":"ConfigObject.bypassCSP","description":"Disable cors see: https://pptr.dev/#?product=Puppeteer&version=v3.0.4&show=api-pagesetbypasscspenabled If you are having an issue with sending media try to set this to true. Otherwise leave it set to false.","default":"`false`","type":"boolean"},"chromiumArgs":{"items":{"title":"ConfigObject.chromiumArgs.[]","type":"string"},"title":"ConfigObject.chromiumArgs","description":"This allows you to pass any array of custom chrome/chromium argument strings to the puppeteer instance.\nYou can find all possible arguements [here](https://peter.sh/experiments/chromium-command-line-switches/).","type":"array"},"skipBrokenMethodsCheck":{"title":"ConfigObject.skipBrokenMethodsCheck","description":"If set to true, skipBrokenMethodsCheck will bypass the health check before startup. It is highly suggested to not set this to true.","default":"`false`","type":"boolean"},"skipUpdateCheck":{"title":"ConfigObject.skipUpdateCheck","description":"If set to true, `skipUpdateCheck` will bypass the latest version check. This saves some time on boot (around 150 ms).","default":"`false`","type":"boolean"},"sessionId":{"title":"ConfigObject.sessionId","description":"This is the name of the session. You have to make sure that this is unique for every session.","default":"`session`","type":"string"},"licenseKey":{"$ref":"#/components/schemas/LicenseKey","title":"ConfigObject.licenseKey","description":"In order to unlock the functionality to send texts to unknown numbers, you need a License key.\nOne License Key is valid for each number. Each License Key starts from £5 per month.\n\nPlease check README for instructions on how to get a license key.\n\nNotes:\n1. You can change the number assigned to that License Key at any time, just message me the new number on the private discord channel.\n2. In order to cancel your License Key, simply stop your membership."},"customUserAgent":{"title":"ConfigObject.customUserAgent","description":"You may set a custom user agent. However, due to recent developments, this is not really neccessary any more.","type":"string"},"devtools":{"anyOf":[{"title":"ConfigObject.devtools","type":"boolean"},{"$ref":"#/components/schemas/DevTools","title":"ConfigObject.devtools"}],"title":"ConfigObject.devtools","description":"You can enable remote devtools by setting this to trye. If you set this to true there will be security on the devtools url.\nIf you want, you can also pass a username & password."},"blockCrashLogs":{"title":"ConfigObject.blockCrashLogs","description":"Setting this to true will block any network calls to crash log servers. This should keep anything you do under the radar.","default":"`true`","type":"boolean"},"cacheEnabled":{"title":"ConfigObject.cacheEnabled","description":"Setting this to false turn off the cache. This may improve memory usage.","default":"`false`","type":"boolean"},"browserRevision":{"title":"ConfigObject.browserRevision","description":"This is the specific browser revision to be downlaoded and used. You can find browser revision strings here: http://omahaproxy.appspot.com/\nLearn more about it here: https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#class-browserfetcher\nIf you're having trouble with sending images, try '737027'.\nIf you go too far back things will start breaking !!!!!!\nNOTE: THIS WILL OVERRIDE useChrome and executablePath. ONLY USE THIS IF YOU KNOW WHAT YOU ARE DOING.","type":"string"},"throwErrorOnTosBlock":{"title":"ConfigObject.throwErrorOnTosBlock","description":"Setting this to true will throw an error if a session is not able to get a QR code or is unable to restart a session.","type":"boolean"},"headless":{"title":"ConfigObject.headless","description":"By default, all instances of @open-wa/wa-automate are headless (i.e you don't see a chrome window open), you can set this to false to show the chrome/chromium window.","default":"`true`","type":"boolean"},"autoRefresh":{"title":"ConfigObject.autoRefresh","description":"@deprecated THIS IS LOCKED TO `true` AND CANNOT BE TURNED OFF. PLEASE SEE [[authTimeout]]\n\nSetting this to true will result in new QR codes being generated if the end user takes too long to scan the QR code.","default":"`true`","type":"boolean"},"qrRefreshS":{"title":"ConfigObject.qrRefreshS","description":"@deprecated This now has no effect\n\nThis determines the interval at which to refresh the QR code. By default, WA updates the qr code every 18-19 seconds so make sure this value is set to UNDER 18 seconds!!","type":"number"},"qrTimeout":{"title":"ConfigObject.qrTimeout","description":"This determines how long the process should wait for a QR code to be scanned before killing the process entirely. To have the system wait continuously, set this to `0`.","default":"60","type":"number"},"waitForRipeSessionTimeout":{"title":"ConfigObject.waitForRipeSessionTimeout","description":"This determines how long the process should wait for a session to load fully before continuing the launch process.\nSet this to 0 to wait forever. Default is 5 seconds.","default":"5","type":"number"},"executablePath":{"title":"ConfigObject.executablePath","description":"Some features, like video upload, do not work without a chrome instance. Set this to the path of your chrome instance or you can use `useChrome:true` to automatically detect a chrome instance for you. Please note, this overrides `useChrome`.","type":"string"},"useChrome":{"title":"ConfigObject.useChrome","description":"If true, the program will automatically try to detect the instance of chorme on the machine. Please note this DOES NOT override executablePath.","default":"`false`","type":"boolean"},"proxyServerCredentials":{"$ref":"#/components/schemas/ProxyServerCredentials","title":"ConfigObject.proxyServerCredentials","description":"If sent, adds a call to waPage.authenticate with those credentials. Set `corsFix` to true if using a proxy results in CORS errors."},"qrLogSkip":{"title":"ConfigObject.qrLogSkip","description":"If true, skips logging the QR Code to the console.","default":"`false`","type":"boolean"},"restartOnCrash":{"title":"ConfigObject.restartOnCrash","description":"If set, the program will try to recreate itself when the page crashes. You have to pass the function that you want called upon restart. Please note that when the page crashes you may miss some messages.\nE.g:\n```javascript\nconst start = async (client: Client) => {...}\ncreate({\n...\nrestartOnCrash: start,\n...\n})\n```"},"disableSpins":{"title":"ConfigObject.disableSpins","description":"Setting this to true will simplify logs for use within docker containers by disabling spins (will still print raw messages).","default":"`false`","type":"boolean"},"logConsole":{"title":"ConfigObject.logConsole","description":"If true, this will log any console messages from the browser.","default":"`false`","type":"boolean"},"logConsoleErrors":{"title":"ConfigObject.logConsoleErrors","description":"If true, this will log any error messages from the browser instance","default":"`false`","type":"boolean"},"authTimeout":{"title":"ConfigObject.authTimeout","description":"This determines how long the process should wait for the session authentication. If exceeded, checks if phone is out of reach (turned of or without internet connection) and throws an error. It does not relate to the amount of time spent waiting for a qr code scan (see [[qrTimeout]]). To have the system wait continuously, set this to `0`.","default":"`60`","type":"number"},"oorTimeout":{"title":"ConfigObject.oorTimeout","description":"phoneIsOutOfReach check timeout","default":"`60`","type":"number"},"killProcessOnBrowserClose":{"title":"ConfigObject.killProcessOnBrowserClose","description":"Setting this to `true` will kill the whole process when the client is disconnected from the page or if the browser is closed.","default":"`false`","type":"boolean"},"safeMode":{"title":"ConfigObject.safeMode","description":"If true, client will check if the page is valid before each command. If page is not valid, it will throw an error.","default":"`false`","type":"boolean"},"skipSessionSave":{"title":"ConfigObject.skipSessionSave","description":"If true, the process will not save a data.json file. This means that sessions will not be saved and you will need to pass sessionData as a config param or create the session data.json file yourself","default":"`false`","type":"boolean"},"popup":{"title":"ConfigObject.popup","description":"If true, the process will open a browser window where you will see basic event logs and QR codes to authenticate the session. Usually it will open on port 3000. It can also be set to a preferred port.\n\nYou can also get the QR code png at (if localhost and port 3000):\n\n`http://localhost:3000/qr`\n\nor if you have multiple session:\n\n `http://localhost:3000/qr?sessionId=[sessionId]`","default":"`false | 3000`","anyOf":[{"type":"boolean"},{"type":"number"}]},"qrPopUpOnly":{"title":"ConfigObject.qrPopUpOnly","description":"This needs to be used in conjuction with `popup`, if `popup` is not true or a number (representing a desired port) then this will not work.\n\nSetting this to true will make sure that only the qr code png is served via the web server. This is useful if you do not need the whole status page.\n\nAs mentioned in [popup](#popup), the url for the qr code is `http://localhost:3000/qr` if the port is 3000.","type":"boolean"},"inDocker":{"title":"ConfigObject.inDocker","description":"If true, the process will try infer as many config variables as possible from the environment variables. The format of the variables are as below:\n```\nsessionData ==> WA_SESSION_DATA\nsessionDataPath ==> WA_SESSION_DATA_PATH\nsessionId ==> WA_SESSION_ID\ncustomUserAgent ==> WA_CUSTOM_USER_AGENT\nblockCrashLogs ==> WA_BLOCK_CRASH_LOGS\nblockAssets ==> WA_BLOCK_ASSETS\ncorsFix ==> WA_CORS_FIX\ncacheEnabled ==> WA_CACHE_ENABLED\nheadless ==> WA_HEADLESS\nqrTimeout ==> WA_QR_TIMEOUT\nuseChrome ==> WA_USE_CHROME\nqrLogSkip ==> WA_QR_LOG_SKIP\ndisableSpins ==> WA_DISABLE_SPINS\nlogConsole ==> WA_LOG_CONSOLE\nlogConsoleErrors==> WA_LOG_CONSOLE_ERRORS\nauthTimeout ==> WA_AUTH_TIMEOUT\nsafeMode ==> WA_SAFE_MODE\nskipSessionSave ==> WA_SKIP_SESSION_SAVE\npopup ==> WA_POPUP\nlicensekey ==> WA_LICENSE_KEY\n```","default":"`false`","type":"boolean"},"qrQuality":{"$ref":"#/components/schemas/QRQuality","title":"ConfigObject.qrQuality","description":"The output quality of the qr code during authentication. This can be any increment of 0.1 from 0.1 to 1.0.","default":"`1.0`"},"qrFormat":{"$ref":"#/components/schemas/QRFormat","title":"ConfigObject.qrFormat","description":"The output format of the qr code. `png`, `jpeg` or `webm`.","default":"`png`"},"hostNotificationLang":{"$ref":"#/components/schemas/NotificationLanguage","title":"ConfigObject.hostNotificationLang","description":"The language of the host notification. See: https://github.com/open-wa/wa-automate-nodejs/issues/709#issuecomment-673419088"},"blockAssets":{"title":"ConfigObject.blockAssets","description":"Setting this to true will block all assets from loading onto the page. This may result in some load time improvements but also increases instability.","default":"`false`","type":"boolean"},"keepUpdated":{"title":"ConfigObject.keepUpdated","description":"[ALPHA FEATURE - ONLY IMPLEMENTED FOR TESTING - DO NOT USE IN PRODUCTION YET]\nSetting this to true will result in the library making sure it is always starting with the latest version of itself. This overrides `skipUpdateCheck`.","default":"`false`","type":"boolean"},"resizable":{"title":"ConfigObject.resizable","description":"Syncs the viewport size with the window size which is how normal browsers act. Only relevant when `headless: false` and this overrides `viewport` config.","default":"`true`","type":"boolean"},"viewport":{"properties":{"width":{"title":"ConfigObject.viewport.width","description":"Page width in pixels","default":"`1440`","type":"number"},"height":{"title":"ConfigObject.viewport.height","description":"Page height in pixels","default":"`900`","type":"number"}},"additionalProperties":false,"title":"ConfigObject.viewport","description":"Set the desired viewport height and width. For CLI, use [width]x[height] format. E.g `--viewport 1920x1080`.","type":"object"},"legacy":{"title":"ConfigObject.legacy","description":"As the library is constantly evolving, some parts will be replaced with more efficient and improved code. In some of the infinite edge cases these new changes may not work for you. Set this to true to roll back on 'late beta' features. The reason why legacy is false by default is that in order for features to be tested they have to be released and used by everyone to find the edge cases and fix them.","default":"`false`","type":"boolean"},"deleteSessionDataOnLogout":{"title":"ConfigObject.deleteSessionDataOnLogout","description":"Deletes the session data file (if found) on logout event. This results in a quicker login when you restart the process.","default":"`false`","type":"boolean"},"killProcessOnTimeout":{"title":"ConfigObject.killProcessOnTimeout","description":"If set to true, the system will kill the whole node process when either an [[authTimeout]] or a [[qrTimeout]] has been reached. This is useful to prevent hanging processes.","default":"`false`","type":"boolean"},"killProcessOnBan":{"title":"ConfigObject.killProcessOnBan","description":"If set to true, the system will kill the whole node process when a \"TEMPORARY BAN\" is detected. This is useful to prevent hanging processes.\nIt is `true` by default because it is a very rare event and it is better to kill the process than to leave it hanging.","default":"`true`","type":"boolean"},"corsFix":{"title":"ConfigObject.corsFix","description":"Setting this to true will bypass web security. DO NOT DO THIS IF YOU DO NOT HAVE TO. CORS issue may arise when using a proxy.","default":"`false`","type":"boolean"},"callTimeout":{"title":"ConfigObject.callTimeout","description":"Amount of time (in ms) to wait for a client method (specifically methods that interact with the WA web session) to resolve. If a client method results takes longer than the timout value then it will result in a [[PageEvaluationTimeout]] error.\n\nIf you get this error, it does not automatically mean that the method failed - it just stops your program from waiting for a client method to resolve.\n\nThis is useful if you do not rely on the results of a client method (e.g the message ID).\n\nIf set to `0`, the process will wait indefinitely for a client method to resolve.","default":"0","type":"number"},"screenshotOnInitializationBrowserError":{"title":"ConfigObject.screenshotOnInitializationBrowserError","description":"When true, this option will take a screenshot of the browser when an unexpected error occurs within the browser during `create` initialization. The path will be `[working directory]/logs/[session ID]/[start timestamp]/[timestamp].jpg`","default":"`false`","type":"boolean"},"eventMode":{"title":"ConfigObject.eventMode","description":"Setting listeners may not be your cup of tea. With eventMode, all [[SimpleListener]] events will be registered automatically and be filed via the built in Events Listener.\n\nThis is useful because you can register/deregister the event listener as needed whereas the legacy method of setting callbacks are only be set once","default":"`true`;","type":"boolean"},"logFile":{"title":"ConfigObject.logFile","description":"If true, the system will automatically create a log of all processes relating to actions sent to the web session.\n\nThe location of the file will be relative to the process directory (pd)\n\n`[pd]/[sessionId]/[start timestamp].log`","default":"false","type":"boolean"},"idCorrection":{"title":"ConfigObject.idCorrection","description":"When true, the system will attempt to correct chatIds and groupChatIds. This means you can ignore `@c.us` and `@g.us` distinctions in some parameters.","default":"false","type":"boolean"},"stickerServerEndpoint":{"title":"ConfigObject.stickerServerEndpoint","description":"Redundant until self-hostable sticker server is available.","default":"`https://sticker-api.openwa.dev`","anyOf":[{"type":"string"},{"type":"boolean"}]},"ghPatch":{"title":"ConfigObject.ghPatch","description":"This will force the library to use the default cached raw github link for patches to shave a few hundred milliseconds from your launch time. If you use this option, you will need to wait about 5 minutes before trying out new patches.","default":"`false`","type":"boolean"},"cachedPatch":{"title":"ConfigObject.cachedPatch","description":"Setting this to `true` will save a local copy of the patches.json file (as patches.ignore.data.json) which will be used in subsequent instantiations of the session. While the rest of the launch procedure is running, the library will fetch and save a recent version of the patches to ensure your patches don't go stale. This will be ignored if the cached patches are more than a day old.","default":"`false`","type":"boolean"},"logDebugInfoAsObject":{"title":"ConfigObject.logDebugInfoAsObject","description":"Setting `this` to true will replace the `console.table` with a stringified logging of the debug info object instead. This would be useful to set for smaller terminal windows. If `disableSpins` is `true` then this will also be `true`.","default":"`false`","type":"boolean"},"logInternalEvents":{"title":"ConfigObject.logInternalEvents","description":"This will make the library log all internal wa web events to the console. This is useful for debugging purposes. DO NOT TURN THIS ON UNLESS ASKED TO.","type":"boolean"},"killClientOnLogout":{"title":"ConfigObject.killClientOnLogout","description":"Kill the client when a logout is detected","default":"`false`","type":"boolean"},"throwOnExpiredSessionData":{"title":"ConfigObject.throwOnExpiredSessionData","description":"This will make the `create` command return `false` if the detected session data is expired.\n\nThis will mean, the process will not attempt to automatically get a new QR code.","default":"`false`","type":"boolean"},"useNativeProxy":{"title":"ConfigObject.useNativeProxy","description":"Some sessions may experience issues with sending media when using proxies. Using the native proxy system instead of the recommended 3rd party library may fix these issues.","default":"`false`","type":"boolean"},"raspi":{"title":"ConfigObject.raspi","description":"Set this to `true` to make the library work on Raspberry Pi OS.\n\nMake sure to run the following command before running the library the first time:\n\n```\n> sudo apt update -y && sudo apt install chromium-browser chromium-codecs-ffmpeg -y && sudo apt upgrade\n```\n\nIf you're using the CLI, you can set this value to `true` by adding the following flag to the CLI command\n\n```\n> npx @open-wa/wa-automate ... --raspi\n```","default":"`false`","type":"boolean"},"pQueueDefault":{"title":"ConfigObject.pQueueDefault","description":"Default pqueue options applied to all listeners that can take pqueue options as a second optional parameter. For now, this only includes `onMessage` and `onAnyMessage`.\n\nSee: https://github.com/sindresorhus/p-queue#options\n\nExample: process 5 events within every 3 seconds window. Make sure to only process at most 2 at any one time. Make sure there is at least 100ms between each event processing.\n\n```javascript\n {\n intervalCap: 5, //process 5 events\n interval: 3000, //within every three second window\n concurrency: 2, //make sure to process, at most, 2 events at any one time\n timeout: 100, //make sure there is a 100ms gap between each event processing.\n carryoverConcurrencyCount: true //If there are more than 5 events in that period, process them within the next 3 second period. Make sure this is always set to true!!!\n }\n```","default":"`undefined`"},"messagePreprocessor":{"title":"ConfigObject.messagePreprocessor","description":"Set a preprocessor, or multiple chained preprocessors, for messages. See [MPConfigType](/) for more info.\n\noptions: `SCRUB`, `BODY_ONLY`, `AUTO_DECRYPT`, `AUTO_DECRYPT_SAVE`, `UPLOAD_CLOUD`.","default":"`undefined`"},"preprocFilter":{"title":"ConfigObject.preprocFilter","description":"Set an array filter to be used with messagePreprocessor to limit which messages are preprocessed.\n\nE.g if you want to scrub all messages that are not from a group, you can do the following:\n`\"m=>!m.isGroupMsg\"`","default":"`undefined`","type":"string"},"cloudUploadOptions":{"properties":{"provider":{"$ref":"#/components/schemas/CLOUD_PROVIDERS","title":"ConfigObject.cloudUploadOptions.provider","description":"`AWS`, `GCP` or `WASABI`\n\nenv: `OW_CLOUD_ACCESS_KEY_ID`"},"accessKeyId":{"title":"ConfigObject.cloudUploadOptions.accessKeyId","description":"S3 compatible access key ID.\n\ne.g: `AKIAIOSFODNN7EXAMPLE` or `GOOGTS7C7FUP3AIRVJTE2BCD`\n\nenv: `OW_CLOUD_ACCESS_KEY_ID`","type":"string"},"secretAccessKey":{"title":"ConfigObject.cloudUploadOptions.secretAccessKey","description":"S3 compatible secret access key.\n\ne.g `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`\n\nenv: `OW_CLOUD_SECRET_ACCESS_KEY`","type":"string"},"bucket":{"title":"ConfigObject.cloudUploadOptions.bucket","description":"Bucket name\n\nenv: `OW_CLOUD_BUCKET`","type":"string"},"region":{"title":"ConfigObject.cloudUploadOptions.region","description":"Bucket region.\n\nNot required for `GCP` provider\n\nenv: `OW_CLOUD_REGION`","type":"string"},"ignoreHostAccount":{"title":"ConfigObject.cloudUploadOptions.ignoreHostAccount","description":"Ignore processing of messages that are sent by the host account itself\n\nenv: `OW_CLOUD_IGNORE_HOST`","type":"boolean"},"directory":{"anyOf":[{"$ref":"#/components/schemas/DIRECTORY_STRATEGY","title":"ConfigObject.cloudUploadOptions.directory"},{"title":"ConfigObject.cloudUploadOptions.directory","type":"string"}],"title":"ConfigObject.cloudUploadOptions.directory","description":"The directory strategy to use when uploading files. Or just set it to a custom directory string.\n\nenv: `OW_DIRECTORY`"},"public":{"title":"ConfigObject.cloudUploadOptions.public","description":"Setting this to true will make the uploaded file public","type":"boolean"},"headers":{"additionalProperties":{"type":"string"},"title":"ConfigObject.cloudUploadOptions.headers","description":"Extra headers to add to the upload request","type":"object"}},"required":["provider","accessKeyId","secretAccessKey","bucket"],"additionalProperties":false,"title":"ConfigObject.cloudUploadOptions","description":"REQUIRED IF `messagePreprocessor` IS SET TO `UPLOAD_CLOUD`.\n\nThis can be set via the config or the corresponding environment variables.","type":"object"},"onError":{"$ref":"#/components/schemas/OnError","title":"ConfigObject.onError","description":"What to do when an error is detected on a client method.","default":"`OnError.NOTHING`"},"multiDevice":{"title":"ConfigObject.multiDevice","description":"Please note that multi-device is still in beta so a lot of things may not work. It is HIGHLY suggested to NOT use this in production!!!!\n\nSet this to true if you're using the multidevice beta.","default":"`true`\n:::danger\nSome features (e.g [[sendLinkWithAutoPreview]]) **do not** work with multi-device beta. Check [this `api`](#).\n:::","type":"boolean"},"sessionDataBucketAuth":{"title":"ConfigObject.sessionDataBucketAuth","description":"Base64 encoded S3 Bucket & Authentication object for session data files. The object should be in the same format as cloudUploadOptions.","type":"string"},"autoEmoji":{"anyOf":[{"title":"ConfigObject.autoEmoji","type":"string"},{"title":"ConfigObject.autoEmoji","enum":[false],"type":"boolean"}],"title":"ConfigObject.autoEmoji","description":"Set the automatic emoji detection character. Set this to `false` to disable auto emoji. Default is `:`.","default":"`:`"},"maxChats":{"title":"ConfigObject.maxChats","description":"Set the maximum amount of chats to be present in a session.","type":"number"},"maxMessages":{"title":"ConfigObject.maxMessages","description":"Set the maximum amount of messages to be present in a session.","type":"number"},"discord":{"title":"ConfigObject.discord","description":"Your Discord ID to get onto the sticker leaderboard!","type":"string"},"ignoreNuke":{"title":"ConfigObject.ignoreNuke","description":"Don't implicitly determine if the host logged out.","type":"boolean"},"ensureHeadfulIntegrity":{"title":"ConfigObject.ensureHeadfulIntegrity","description":"Makes sure the headless session is usable even on first login.\nHeadful sessions are ususally only usable on reauthentication.","type":"boolean"},"waitForRipeSession":{"title":"ConfigObject.waitForRipeSession","description":"wait for a valid headful session. Not required in recent versions.\ndefault: `true`","type":"boolean"},"qrMax":{"title":"ConfigObject.qrMax","description":"Automatically kill the process after a set amount of qr codes","type":"number"},"ezqr":{"title":"ConfigObject.ezqr","description":"Expose a URL where you can easily scan the qr code","type":"boolean"},"logging":{"items":{"$ref":"#/components/schemas/ConfigLogTransport","title":"ConfigObject.logging.[]"},"title":"ConfigObject.logging","description":"An array of [winston](https://github.com/winstonjs/winston/blob/master/docs/transports.md#additional-transports) logging transport configurations.\n\n[Check this discussion to see how to set up logging](https://github.com/open-wa/wa-automate-nodejs/discussions/2373)","type":"array"},"linkParser":{"title":"ConfigObject.linkParser","description":"The URL of your instance of [serverless meta grabber](https://github.com/RemiixInc/meta-grabber-serverless) by [RemiixInc](https://github.com/RemiixInc).\n\ndefault: `https://link.openwa.cloud/api`","type":"string"},"aggressiveGarbageCollection":{"title":"ConfigObject.aggressiveGarbageCollection","description":"Setting this to true will run `gc()` on before every command sent to the browser.\n\nThis is experimental and may not work or it may have unforeseen sideeffects.","type":"boolean"}},"additionalProperties":{},"title":"ConfigObject","type":"object"},"AdvancedConfig":{"allOf":[{"$ref":"#/components/schemas/ConfigObject"},{"properties":{"licenseKey":{"$ref":"#/components/schemas/LicenseKeyConfig","title":"licenseKey"}},"required":["licenseKey"],"additionalProperties":false,"type":"object"}],"title":"AdvancedConfig"},"LicenseKey":{"title":"LicenseKey","type":"string"},"LicenseKeyConfig":{"anyOf":[{"$ref":"#/components/schemas/LicenseKeyConfigFunction","title":"LicenseKeyConfig"},{"$ref":"#/components/schemas/LicenseKeyConfigObject","title":"LicenseKeyConfig"},{"$ref":"#/components/schemas/LicenseKey","title":"LicenseKeyConfig"}],"title":"LicenseKeyConfig"},"NumberCheck":{"properties":{"id":{"$ref":"#/components/schemas/Id","title":"NumberCheck.id"},"status":{"enum":[200,404],"title":"NumberCheck.status","type":"number"},"isBusiness":{"title":"NumberCheck.isBusiness","type":"boolean"},"canReceiveMessage":{"title":"NumberCheck.canReceiveMessage","type":"boolean"},"numberExists":{"title":"NumberCheck.numberExists","type":"boolean"}},"required":["id","status","isBusiness","canReceiveMessage","numberExists"],"additionalProperties":false,"title":"NumberCheck","type":"object"},"BizCategory":{"properties":{"id":{"title":"BizCategory.id","type":"string"},"localized_display_name":{"title":"BizCategory.localized_display_name","type":"string"}},"required":["id","localized_display_name"],"additionalProperties":false,"title":"BizCategory","type":"object"},"BizProfileOptions":{"properties":{"commerceExperience":{"enum":["catalog","none","shop"],"title":"BizProfileOptions.commerceExperience","type":"string"},"cartEnabled":{"title":"BizProfileOptions.cartEnabled","type":"boolean"}},"required":["commerceExperience","cartEnabled"],"additionalProperties":false,"title":"BizProfileOptions","type":"object"},"BusinessHours":{"properties":{"config":{"title":"BusinessHours.config"},"timezone":{"title":"BusinessHours.timezone","type":"string"}},"required":["config","timezone"],"additionalProperties":false,"title":"BusinessHours","type":"object"},"BusinessProfile":{"properties":{"id":{"$ref":"#/components/schemas/ContactId","title":"BusinessProfile.id","description":"The Contact ID of the business"},"tag":{"title":"BusinessProfile.tag","description":"Some special string that identifies the business (?)","type":"string"},"description":{"title":"BusinessProfile.description","description":"The business description","type":"string"},"categories":{"items":{"$ref":"#/components/schemas/BizCategory","title":"BusinessProfile.categories.[]"},"title":"BusinessProfile.categories","description":"The business' categories","type":"array"},"profileOptions":{"$ref":"#/components/schemas/BizProfileOptions","title":"BusinessProfile.profileOptions","description":"The business' profile options"},"email":{"title":"BusinessProfile.email","description":"The business' email address","type":"string"},"website":{"items":{"title":"BusinessProfile.website.[]","type":"string"},"title":"BusinessProfile.website","description":"Array of strings that represent the business' websites","type":"array"},"businessHours":{"$ref":"#/components/schemas/BusinessHours","title":"BusinessProfile.businessHours","description":"The operating hours of the business"},"catalogStatus":{"title":"BusinessProfile.catalogStatus","description":"The status of the business' catalog","type":"string"},"address":{"title":"BusinessProfile.address","description":"The address of the business","type":"string"},"fbPage":{"title":"BusinessProfile.fbPage","description":"The facebook page of the business"},"igProfessional":{"title":"BusinessProfile.igProfessional","description":"The instagram profile of the business"},"isProfileLinked":{"title":"BusinessProfile.isProfileLinked","type":"boolean"},"coverPhoto":{"properties":{"id":{"title":"BusinessProfile.coverPhoto.id","description":"The id of the cover photo","type":"string"},"url":{"title":"BusinessProfile.coverPhoto.url","description":"The URL of the cover photo. It might download as an .enc but just change the extension to .jpg","type":"string"}},"required":["id","url"],"additionalProperties":false,"title":"BusinessProfile.coverPhoto","type":"object"},"latitude":{"title":"BusinessProfile.latitude","description":"The latitude of the business location if set","type":"number"},"longitude":{"title":"BusinessProfile.longitude","description":"The longitude of the business location if set","type":"number"}},"required":["id","tag","description","categories","profileOptions","email","website","businessHours","catalogStatus","address","fbPage","igProfessional","isProfileLinked","coverPhoto","latitude","longitude"],"additionalProperties":false,"title":"BusinessProfile","type":"object"},"Contact":{"properties":{"formattedName":{"title":"Contact.formattedName","type":"string"},"id":{"$ref":"#/components/schemas/ContactId","title":"Contact.id"},"isBusiness":{"title":"Contact.isBusiness","type":"boolean"},"isEnterprise":{"title":"Contact.isEnterprise","description":"Most likely true when the account has a green tick. See `verifiedLevel` also.","type":"boolean"},"isMe":{"title":"Contact.isMe","type":"boolean"},"isMyContact":{"title":"Contact.isMyContact","type":"boolean"},"isPSA":{"title":"Contact.isPSA","type":"boolean"},"isUser":{"title":"Contact.isUser","type":"boolean"},"isWAContact":{"title":"Contact.isWAContact","type":"boolean"},"labels":{"items":{"title":"Contact.labels.[]","type":"string"},"title":"Contact.labels","type":"array"},"msgs":{"items":{"$ref":"#/components/schemas/Message","title":"Contact.msgs.[]"},"title":"Contact.msgs","type":"array"},"name":{"title":"Contact.name","type":"string"},"plaintextDisabled":{"title":"Contact.plaintextDisabled","type":"boolean"},"profilePicThumbObj":{"properties":{"eurl":{"title":"Contact.profilePicThumbObj.eurl","type":"string"},"id":{"$ref":"#/components/schemas/Id","title":"Contact.profilePicThumbObj.id"},"img":{"title":"Contact.profilePicThumbObj.img","type":"string"},"imgFull":{"title":"Contact.profilePicThumbObj.imgFull","type":"string"},"raw":{"title":"Contact.profilePicThumbObj.raw","type":"string"},"tag":{"title":"Contact.profilePicThumbObj.tag","type":"string"}},"required":["eurl","id","img","imgFull","raw","tag"],"additionalProperties":false,"title":"Contact.profilePicThumbObj","type":"object"},"pushname":{"title":"Contact.pushname","type":"string"},"shortName":{"title":"Contact.shortName","type":"string"},"statusMute":{"title":"Contact.statusMute","type":"boolean"},"type":{"title":"Contact.type","type":"string"},"verifiedLevel":{"title":"Contact.verifiedLevel","description":"0 = not verified\n2 = verified (most likely represents a blue tick)","type":"number"},"verifiedName":{"title":"Contact.verifiedName","description":"The business account name verified by WA.","type":"string"},"isOnline":{"title":"Contact.isOnline","type":"boolean"},"lastSeen":{"title":"Contact.lastSeen","type":"number"},"businessProfile":{"$ref":"#/components/schemas/BusinessProfile","title":"Contact.businessProfile","description":"If the contact is a business, the business information will be added to the contact object.\n\nIn some circumstances this will be out of date or lacking certain fields. In those cases you have to use `client.getBusinessProfile`"}},"required":["formattedName","id","isBusiness","isEnterprise","isMe","isMyContact","isPSA","isUser","isWAContact","labels","msgs","name","plaintextDisabled","profilePicThumbObj","pushname","shortName","statusMute","type","verifiedLevel","verifiedName"],"additionalProperties":false,"title":"Contact","type":"object"},"Participant":{"properties":{"contact":{"$ref":"#/components/schemas/Contact","title":"Participant.contact"},"id":{"$ref":"#/components/schemas/NonSerializedId","title":"Participant.id"},"isAdmin":{"title":"Participant.isAdmin","type":"boolean"},"isSuperAdmin":{"title":"Participant.isSuperAdmin","type":"boolean"}},"required":["contact","id","isAdmin","isSuperAdmin"],"additionalProperties":false,"title":"Participant","type":"object"},"GroupMetadata":{"properties":{"id":{"$ref":"#/components/schemas/GroupChatId","title":"GroupMetadata.id","description":"The chat id of the group [[GroupChatId]]"},"creation":{"title":"GroupMetadata.creation","description":"The timestamp of when the group was created","type":"number"},"owner":{"$ref":"#/components/schemas/NonSerializedId","title":"GroupMetadata.owner","description":"The id of the owner of the group [[ContactId]]"},"participants":{"items":{"$ref":"#/components/schemas/Participant","title":"GroupMetadata.participants.[]"},"title":"GroupMetadata.participants","description":"An array of participants in the group","type":"array"},"pendingParticipants":{"items":{"$ref":"#/components/schemas/Participant","title":"GroupMetadata.pendingParticipants.[]"},"title":"GroupMetadata.pendingParticipants","description":"Unknown.","type":"array"},"desc":{"title":"GroupMetadata.desc","description":"The description of the group","type":"string"},"descOwner":{"$ref":"#/components/schemas/ContactId","title":"GroupMetadata.descOwner","description":"The account that set the description last."},"trusted":{"title":"GroupMetadata.trusted","type":"boolean"},"suspended":{"title":"GroupMetadata.suspended","description":"Not sure what this represents","type":"boolean"},"support":{"title":"GroupMetadata.support","description":"Not sure what this represents","type":"boolean"},"isParentGroup":{"title":"GroupMetadata.isParentGroup","description":"Is this group a parent group (a.k.a community)","type":"boolean"},"groupType":{"enum":["DEAFULT","SUBGROUP","COMMUNITY"],"title":"GroupMetadata.groupType","description":"The type of group","type":"string"},"defaultSubgroup":{"title":"GroupMetadata.defaultSubgroup","description":"Communities have a default group chat","type":"boolean"},"isParentGroupClosed":{"title":"GroupMetadata.isParentGroupClosed","type":"boolean"},"joinedSubgroups":{"items":{"$ref":"#/components/schemas/GroupId","title":"GroupMetadata.joinedSubgroups.[]"},"title":"GroupMetadata.joinedSubgroups","description":"List of Group IDs that the host account has joined as part of this community","type":"array"}},"required":["id","creation","owner","participants","pendingParticipants","groupType","defaultSubgroup","isParentGroupClosed","joinedSubgroups"],"additionalProperties":false,"title":"GroupMetadata","type":"object"},"ParticipantChangedEventModel":{"properties":{"by":{"$ref":"#/components/schemas/ContactId","title":"ParticipantChangedEventModel.by"},"action":{"$ref":"#/components/schemas/groupChangeEvent","title":"ParticipantChangedEventModel.action"},"who":{"items":{"$ref":"#/components/schemas/ContactId","title":"ParticipantChangedEventModel.who.[]"},"title":"ParticipantChangedEventModel.who","type":"array"},"chat":{"$ref":"#/components/schemas/ChatId","title":"ParticipantChangedEventModel.chat"}},"required":["by","action","who","chat"],"additionalProperties":false,"title":"ParticipantChangedEventModel","type":"object"},"NewCommunityGroup":{"properties":{"subject":{"title":"NewCommunityGroup.subject","type":"string"},"icon":{"$ref":"#/components/schemas/DataURL","title":"NewCommunityGroup.icon"},"ephemeralDuration":{"title":"NewCommunityGroup.ephemeralDuration","type":"number"}},"required":["subject"],"additionalProperties":false,"title":"NewCommunityGroup","description":"Used when creating a new community with.","type":"object"},"GenericGroupChangeEvent":{"properties":{"author":{"$ref":"#/components/schemas/Contact","title":"GenericGroupChangeEvent.author","description":"The contact who triggered this event. (E.g the contact who changed the group picture)"},"body":{"title":"GenericGroupChangeEvent.body","description":"Some more information about the event","type":"string"},"groupMetadata":{"$ref":"#/components/schemas/GroupMetadata","title":"GenericGroupChangeEvent.groupMetadata"},"groupPic":{"title":"GenericGroupChangeEvent.groupPic","description":"Base 64 encoded image","type":"string"},"id":{"$ref":"#/components/schemas/MessageId","title":"GenericGroupChangeEvent.id"},"type":{"enum":["picutre","create","delete","subject","revoke_invite","description","restrict","announce","no_frequently_forwarded","announce_msg_bounce","add","remove","demote","promote","invite","leave","modify","v4_add_invite_sent","v4_add_invite_join","growth_locked","growth_unlocked","linked_group_join"],"title":"GenericGroupChangeEvent.type","description":"Type of the event","type":"string"}},"required":["author","body","groupMetadata","groupPic","id","type"],"additionalProperties":false,"title":"GenericGroupChangeEvent","type":"object"},"Id":{"properties":{"server":{"title":"Id.server","type":"string"},"user":{"title":"Id.user","type":"string"},"_serialized":{"title":"Id._serialized","type":"string"}},"required":["server","user","_serialized"],"additionalProperties":false,"title":"Id","type":"object"},"EasyApiResponse":{"properties":{"success":{"title":"EasyApiResponse.success","type":"boolean"},"response":{"title":"EasyApiResponse.response"}},"required":["success","response"],"additionalProperties":false,"title":"EasyApiResponse","type":"object"},"Label":{"properties":{"id":{"title":"Label.id","description":"The internal ID of the label. Usually a number represented as a string e.g \"1\"","type":"string"},"name":{"title":"Label.name","description":"The text contents of the label","type":"string"},"items":{"items":{"properties":{"type":{"enum":["Chat","Contact","Message"],"title":"Label.items.[].type","description":"Labels can be applied to chats, contacts or individual messages. This represents the type of object the label is attached to.","type":"string"},"id":{"anyOf":[{"$ref":"#/components/schemas/ContactId","title":"Label.items.[].id"},{"$ref":"#/components/schemas/ChatId","title":"Label.items.[].id"},{"$ref":"#/components/schemas/MessageId","title":"Label.items.[].id"}],"title":"Label.items.[].id","description":"The ID of the object that the label is atteched to."}},"required":["type","id"],"additionalProperties":false,"title":"Label.items.[]","type":"object"},"title":"Label.items","description":"The items that are tagged with this label","type":"array"}},"required":["id","name","items"],"additionalProperties":false,"title":"Label","type":"object"},"StickerMetadata":{"properties":{"author":{"title":"StickerMetadata.author","description":"The author of the sticker","default":"``","type":"string"},"pack":{"title":"StickerMetadata.pack","description":"The pack of the sticker","default":"``","type":"string"},"removebg":{"anyOf":[{"title":"StickerMetadata.removebg","type":"boolean"},{"title":"StickerMetadata.removebg","enum":["HQ"],"type":"string"}],"title":"StickerMetadata.removebg","description":"ALPHA FEATURE - NO GUARANTEES IT WILL WORK AS EXPECTED:\n\n[REQUIRES AN INSIDERS LICENSE-KEY](https://gum.co/open-wa?tier=Insiders%20Program)\n\nAttempt to remove the background of the sticker. Only valid for paid licenses.\n\noptions:\n\n `true` - remove background after resizing\n\n `HQ` - remove background before resizing (i.e on original photo)","default":"`false`"},"keepScale":{"title":"StickerMetadata.keepScale","description":"Setting this to `true` will skip the resizing/square-cropping of the sticker. It will instead 'letterbox' the image with a transparent background.","type":"boolean"},"circle":{"title":"StickerMetadata.circle","description":"Applies a circular mask to the sticker. Works on images only for now.","type":"boolean"},"discord":{"title":"StickerMetadata.discord","description":"Your Discord ID to get onto the sticker leaderboard!","type":"string"},"cropPosition":{"enum":["top","right top","right","right bottom","bottom","left bottom","left","left top","north","northeast","east","southeast","south","southwest","west","northwest","center","centre","entropy","attention"],"title":"StickerMetadata.cropPosition","description":"Crop position\n\nLearn more: https://sharp.pixelplumbing.com/api-resize","default":"`attention`","type":"string"},"cornerRadius":{"title":"StickerMetadata.cornerRadius","description":"The corner radius of the sticker when `stickerMetadata.circle` is set to true.\n@minimum `1`\n@maximum `100`\n@multipleOf `1`","default":"`100`","type":"number"}},"required":["author","pack"],"additionalProperties":false,"title":"StickerMetadata","type":"object"},"Mp4StickerConversionProcessOptions":{"properties":{"fps":{"title":"Mp4StickerConversionProcessOptions.fps","description":"Desired Frames per second of the sticker output","default":"`10`","type":"number"},"startTime":{"title":"Mp4StickerConversionProcessOptions.startTime","description":"The video start time of the sticker","default":"`00:00:00.0`","type":"string"},"endTime":{"title":"Mp4StickerConversionProcessOptions.endTime","description":"The video end time of the sticker. By default, stickers are made from the first 5 seconds of the video","default":"`00:00:05.0`","type":"string"},"loop":{"title":"Mp4StickerConversionProcessOptions.loop","description":"The amount of times the video loops in the sticker. To save processing time, leave this as 0\ndefault `0`","type":"number"},"crop":{"title":"Mp4StickerConversionProcessOptions.crop","description":"Centres and crops the video.\ndefault `true`","type":"boolean"},"log":{"title":"Mp4StickerConversionProcessOptions.log","description":"Prints ffmpeg logs in the terminal","default":"`false`","type":"boolean"},"square":{"title":"Mp4StickerConversionProcessOptions.square","description":"A number representing the WxH of the output sticker (default `512x512`). Lowering this number is a great way to process longer duration stickers. The max value is `512`.\ndefault `512`","type":"number"}},"additionalProperties":false,"title":"Mp4StickerConversionProcessOptions","type":"object"},"MessagePinDuration":{"enum":["FifteenSeconds","FiveSeconds","OneDay","OneMinute","SevenDays","ThirtyDays"],"title":"MessagePinDuration","type":"string"},"Message":{"properties":{"selectedButtonId":{"title":"Message.selectedButtonId","description":"The ID of the selected button","type":"string"},"id":{"$ref":"#/components/schemas/MessageId","title":"Message.id","description":"The id of the message. Consists of the Chat ID and a unique string.\n\nExample:\n\n```\nfalse_447123456789@c.us_7D914FEA78BE10277743F4B785045C37\n```"},"mId":{"title":"Message.mId","description":"The unique segment of the message id.\n\nExample:\n\n```\n7D914FEA78BE10277743F4B785045C37\n```","type":"string"},"body":{"title":"Message.body","description":"The body of the message. If the message type is `chat` , `body` will be the text of the chat. If the message type is some sort of media, then this body will be the thumbnail of the media.","type":"string"},"device":{"title":"Message.device","description":"The device ID of the device that sent the message. This is only present if the message was sent from host account-linked session. This is useful for determining if a message was sent from a different mobile device (note that whenever a device) or a desktop session.\n\nNote: This will emit a number for the current controlled session also but the only way to know if the number represents the current session is by checking `local` (it will be `true` if the message was sent from the current session).\n\nIf the device ID is `0` then the message was sent from the \"root\" host account device.\n\nThis might be undefined for incoming messages.","type":"number"},"local":{"title":"Message.local","description":"If the message was sent from this controlled session this will be `true`. This is useful for determining if a message was sent from a different mobile device (note that whenever a device) or a desktop session.","type":"boolean"},"text":{"title":"Message.text","description":"a convenient way to get the main text content from a message.","type":"string"},"type":{"$ref":"#/components/schemas/MessageTypes","title":"Message.type","description":"The type of the message, see [[MessageTypes]]"},"filehash":{"title":"Message.filehash","description":"Used to checking the integrity of the decrypted media.","type":"string"},"mimetype":{"title":"Message.mimetype","type":"string"},"lat":{"title":"Message.lat","description":"The latitude of a location message","type":"string"},"lng":{"title":"Message.lng","description":"The longitude of a location message","type":"string"},"loc":{"title":"Message.loc","description":"The text associated with a location message","type":"string"},"t":{"title":"Message.t","description":"The timestamp of the message","type":"number"},"notifyName":{"title":"Message.notifyName","type":"string"},"from":{"$ref":"#/components/schemas/ChatId","title":"Message.from","description":"The chat from which the message was sent"},"to":{"$ref":"#/components/schemas/ChatId","title":"Message.to","description":"The chat id to which the message is being sent"},"self":{"enum":["in","out"],"title":"Message.self","description":"Indicates whether the message is coming into the session or going out of the session. You can have a message sent by the host account show as `in` when the message was sent from another\nsession or from the host account device itself.","type":"string"},"duration":{"title":"Message.duration","description":"The length of the media in the message, if it exists.","anyOf":[{"type":"string"},{"type":"number"}]},"ack":{"$ref":"#/components/schemas/MessageAck","title":"Message.ack","description":"The acknolwedgement state of a message [[MessageAck]]"},"invis":{"title":"Message.invis","type":"boolean"},"isNewMsg":{"title":"Message.isNewMsg","type":"boolean"},"star":{"title":"Message.star","type":"boolean"},"recvFresh":{"title":"Message.recvFresh","type":"boolean"},"broadcast":{"title":"Message.broadcast","description":"If the message is sent as a broadcast","type":"boolean"},"isForwarded":{"title":"Message.isForwarded","description":"If the message has been forwarded","type":"boolean"},"labels":{"items":{"title":"Message.labels.[]","type":"string"},"title":"Message.labels","description":"The labels associated with the message (used with business accounts)","type":"array"},"mentionedJidList":{"items":{"$ref":"#/components/schemas/ContactId","title":"Message.mentionedJidList.[]"},"title":"Message.mentionedJidList","description":"An array of all mentioned numbers in this message.","type":"array"},"caption":{"title":"Message.caption","description":"If the message is of a media type, it may also have a caption","type":"string"},"sender":{"$ref":"#/components/schemas/Contact","title":"Message.sender","description":"The contact object of the account that sent the message"},"timestamp":{"title":"Message.timestamp","description":"the timestanmp of the message","type":"number"},"filePath":{"title":"Message.filePath","description":"When `config.messagePreprocessor: \"AUTO_DECRYPT_SAVE\"` is set, media is decrypted and saved on disk in a folder called media relative to the current working directory.\n\nThis is the filePath of the decrypted file.","type":"string"},"filename":{"title":"Message.filename","description":"The given filename of the file","type":"string"},"content":{"title":"Message.content","type":"string"},"isGroupMsg":{"title":"Message.isGroupMsg","type":"boolean"},"isMMS":{"title":"Message.isMMS","type":"boolean"},"isMedia":{"title":"Message.isMedia","type":"boolean"},"isNotification":{"title":"Message.isNotification","type":"boolean"},"isPSA":{"title":"Message.isPSA","type":"boolean"},"fromMe":{"title":"Message.fromMe","description":"If the message is from the host account","type":"boolean"},"chat":{"$ref":"#/components/schemas/Chat","title":"Message.chat","description":"The chat object"},"chatId":{"$ref":"#/components/schemas/ChatId","title":"Message.chatId"},"author":{"title":"Message.author","type":"string"},"stickerAuthor":{"title":"Message.stickerAuthor","type":"string"},"stickerPack":{"title":"Message.stickerPack","type":"string"},"clientUrl":{"title":"Message.clientUrl","description":"@deprecated Ironically, you should be using `deprecatedMms3Url` instead","type":"string"},"deprecatedMms3Url":{"title":"Message.deprecatedMms3Url","type":"string"},"isQuotedMsgAvailable":{"title":"Message.isQuotedMsgAvailable","description":"If this message is quoting (replying to) another message","type":"boolean"},"quotedMsg":{"$ref":"#/components/schemas/Message","title":"Message.quotedMsg"},"quotedMsgObj":{"$ref":"#/components/schemas/Message","title":"Message.quotedMsgObj"},"isGroupJoinRequest":{"$ref":"#/components/schemas/GroupChatId","title":"Message.isGroupJoinRequest","description":"When a user requests to join a group wihtin a community the request is received by the host as a message. This boolean will allow you to easily determine if the incoming message is a request to join a group.\n\nIf this is `true` then you need to determine within your own code whether or not to accept the user to the group which is indicated with `quotedRemoteJid` using `addParticipant`."},"senderId":{"title":"Message.senderId","description":"The ID of the message sender","type":"string"},"quotedRemoteJid":{"title":"Message.quotedRemoteJid","description":"The ID of the quoted group. Usually present when a user is requesting to join a group.","type":"string"},"quotedParentGroupJid":{"$ref":"#/components/schemas/GroupChatId","title":"Message.quotedParentGroupJid","description":"The parent group ID (community ID - communities are just groups made up of other groups) of the group represented by `quotedRemoteJid`"},"mediaData":{"title":"Message.mediaData"},"shareDuration":{"title":"Message.shareDuration","type":"number"},"isAnimated":{"title":"Message.isAnimated","type":"boolean"},"ctwaContext":{"properties":{"sourceUrl":{"title":"Message.ctwaContext.sourceUrl","type":"string"},"thumbnail":{"title":"Message.ctwaContext.thumbnail","nullable":true,"type":"string"},"mediaType":{"title":"Message.ctwaContext.mediaType","type":"number"},"isSuspiciousLink":{"title":"Message.ctwaContext.isSuspiciousLink","nullable":true,"type":"boolean"}},"required":["sourceUrl","thumbnail","mediaType","isSuspiciousLink"],"additionalProperties":false,"title":"Message.ctwaContext","type":"object"},"isViewOnce":{"title":"Message.isViewOnce","description":"Is the message a \"view once\" message","type":"boolean"},"quoteMap":{"$ref":"#/components/schemas/QuoteMap","title":"Message.quoteMap","description":"Use this to traverse the quote chain."},"cloudUrl":{"title":"Message.cloudUrl","description":"The URL of the file after being uploaded to the cloud using a cloud upload message preprocessor.","type":"string"},"buttons":{"items":{"$ref":"#/components/schemas/Button","title":"Message.buttons.[]"},"title":"Message.buttons","description":"Buttons associated with the message","type":"array"},"listResponse":{"$ref":"#/components/schemas/Row","title":"Message.listResponse","description":"List response associated with the message"},"list":{"properties":{"\"sections\"":{"items":{"$ref":"#/components/schemas/Section","title":"Message.list.\"sections\".[]"},"title":"Message.list.\"sections\"","type":"array"},"\"title\"":{"title":"Message.list.\"title\"","type":"string"},"\"description\"":{"title":"Message.list.\"description\"","type":"string"},"\"buttonText\"":{"title":"Message.list.\"buttonText\"","type":"string"}},"required":["\"sections\"","\"title\"","\"description\"","\"buttonText\""],"additionalProperties":false,"title":"Message.list","description":"The list associated with the list message","type":"object"},"pollOptions":{"items":{"$ref":"#/components/schemas/PollOption","title":"Message.pollOptions.[]"},"title":"Message.pollOptions","description":"The options of a poll","type":"array"},"reactionByMe":{"$ref":"#/components/schemas/ReactionSender","title":"Message.reactionByMe","description":"The reaction of the host account to this message"},"reactions":{"items":{"properties":{"aggregateEmoji":{"title":"Message.reactions.[].aggregateEmoji","type":"string"},"senders":{"items":{"$ref":"#/components/schemas/ReactionSender","title":"Message.reactions.[].senders.[]"},"title":"Message.reactions.[].senders","description":"The senders of this reaction","type":"array"},"hasReactionByMe":{"title":"Message.reactions.[].hasReactionByMe","description":"If the host account has reacted to this message with this reaction","type":"boolean"},"id":{"title":"Message.reactions.[].id","description":"The message ID of the reaction itself","type":"string"}},"required":["aggregateEmoji","senders","hasReactionByMe","id"],"additionalProperties":false,"title":"Message.reactions.[]","type":"object"},"title":"Message.reactions","type":"array"}},"required":["selectedButtonId","id","mId","body","device","local","text","type","t","notifyName","from","to","self","ack","invis","isNewMsg","star","recvFresh","broadcast","isForwarded","labels","mentionedJidList","caption","sender","timestamp","content","isGroupMsg","isMMS","isMedia","isNotification","isPSA","fromMe","chat","chatId","author","clientUrl","deprecatedMms3Url","isQuotedMsgAvailable","mediaData","shareDuration","isAnimated","isViewOnce","quoteMap","reactions"],"additionalProperties":false,"title":"Message","type":"object"},"ReactionSender":{"properties":{"parentMsgKey":{"$ref":"#/components/schemas/MessageId","title":"ReactionSender.parentMsgKey","description":"The ID of the message being reacted to"},"senderUserJid":{"$ref":"#/components/schemas/ContactId","title":"ReactionSender.senderUserJid","description":"The contact ID of the sender of the reaction"},"msgKey":{"$ref":"#/components/schemas/MessageId","title":"ReactionSender.msgKey","description":"The message ID of the reaction itself"},"reactionText":{"title":"ReactionSender.reactionText","description":"The text of the reaction","type":"string"},"timestamp":{"title":"ReactionSender.timestamp","description":"The timestamp of the reaction","type":"number"},"orphan":{"title":"ReactionSender.orphan","type":"number"},"read":{"title":"ReactionSender.read","description":"If the reaction was seen/read","type":"boolean"},"t":{"title":"ReactionSender.t","description":"The timestamp of the reaction","type":"number"},"id":{"$ref":"#/components/schemas/MessageId","title":"ReactionSender.id","description":"The message ID of the reaction itself"},"isSendFailure":{"title":"ReactionSender.isSendFailure","type":"boolean"},"ack":{"title":"ReactionSender.ack","type":"number"}},"required":["parentMsgKey","senderUserJid","msgKey","reactionText","timestamp","orphan","read","id","isSendFailure"],"additionalProperties":false,"title":"ReactionSender","type":"object"},"PollOption":{"properties":{"name":{"title":"PollOption.name","type":"string"},"localId":{"title":"PollOption.localId","type":"number"}},"required":["name","localId"],"additionalProperties":false,"title":"PollOption","type":"object"},"PollData":{"properties":{"totalVotes":{"title":"PollData.totalVotes","description":"The total amount of votes recorded so far","type":"number"},"pollOptions":{"items":{"allOf":[{"$ref":"#/components/schemas/PollOption"},{"properties":{"count":{"title":"count","type":"number"}},"required":["count"],"additionalProperties":false,"type":"object"}]},"title":"PollData.pollOptions","description":"The poll options and their respective count of votes.","type":"array"},"votes":{"items":{"$ref":"#/components/schemas/PollVote","title":"PollData.votes.[]"},"title":"PollData.votes","description":"An arrray of vote objects","type":"array"},"pollMessage":{"$ref":"#/components/schemas/Message","title":"PollData.pollMessage","description":"The message object of the poll"}},"required":["totalVotes","pollOptions","votes","pollMessage"],"additionalProperties":false,"title":"PollData","type":"object"},"PollVote":{"properties":{"ack":{"title":"PollVote.ack","type":"number"},"id":{"title":"PollVote.id","description":"The message ID of this vote. For some reason this is different from the msgKey and includes exclamaition marks.","type":"string"},"msgKey":{"title":"PollVote.msgKey","description":"The message key of this vote","type":"string"},"parentMsgKey":{"title":"PollVote.parentMsgKey","description":"The Message ID of the original Poll message","type":"string"},"pollOptions":{"items":{"$ref":"#/components/schemas/PollOption","title":"PollVote.pollOptions.[]"},"title":"PollVote.pollOptions","description":"The original poll options available on the poll","type":"array"},"selectedOptionLocalIds":{"items":{"title":"PollVote.selectedOptionLocalIds.[]","type":"number"},"title":"PollVote.selectedOptionLocalIds","description":"The selected option IDs of the voter","type":"array"},"selectedOptionValues":{"items":{"title":"PollVote.selectedOptionValues.[]","type":"string"},"title":"PollVote.selectedOptionValues","description":"The selected option values by this voter","type":"array"},"sender":{"$ref":"#/components/schemas/ContactId","title":"PollVote.sender","description":"The contact ID of the voter"},"senderObj":{"$ref":"#/components/schemas/Contact","title":"PollVote.senderObj","description":"The contact object of the voter"},"senderTimestampMs":{"title":"PollVote.senderTimestampMs","description":"Timestamp of the vote","type":"number"},"stale":{"title":"PollVote.stale","type":"boolean"}},"required":["ack","id","msgKey","parentMsgKey","pollOptions","selectedOptionLocalIds","selectedOptionValues","sender","senderObj","senderTimestampMs","stale"],"additionalProperties":false,"title":"PollVote","type":"object"},"QuoteMap":{"additionalProperties":{"properties":{"body":{"title":"body","description":"The body of the message","type":"string"},"quotes":{"$ref":"#/components/schemas/MessageId","title":"quotes","description":"The message ID of the message that was quoted. Null if no message was quoted."}},"required":["body"],"additionalProperties":false,"type":"object"},"title":"QuoteMap","type":"object"},"MessageInfoInteraction":{"properties":{"id":{"$ref":"#/components/schemas/ContactId","title":"MessageInfoInteraction.id","description":"The contact ID of the contact that interacted with the message."},"t":{"title":"MessageInfoInteraction.t","description":"The timestamp of the interaction. You have to x 1000 to use in a JS Date object.","type":"number"}},"required":["id","t"],"additionalProperties":false,"title":"MessageInfoInteraction","type":"object"},"MessageInfo":{"properties":{"deliveryRemaining":{"title":"MessageInfo.deliveryRemaining","type":"number"},"playedRemaining":{"title":"MessageInfo.playedRemaining","type":"number"},"readRemaining":{"title":"MessageInfo.readRemaining","type":"number"},"delivery":{"items":{"$ref":"#/components/schemas/MessageInfoInteraction","title":"MessageInfo.delivery.[]"},"title":"MessageInfo.delivery","type":"array"},"read":{"items":{"$ref":"#/components/schemas/MessageInfoInteraction","title":"MessageInfo.read.[]"},"title":"MessageInfo.read","type":"array"},"played":{"items":{"$ref":"#/components/schemas/MessageInfoInteraction","title":"MessageInfo.played.[]"},"title":"MessageInfo.played","type":"array"},"id":{"$ref":"#/components/schemas/MessageId","title":"MessageInfo.id","description":"The ID of the message"}},"required":["deliveryRemaining","playedRemaining","readRemaining","delivery","read","played","id"],"additionalProperties":false,"title":"MessageInfo","type":"object"},"CustomProduct":{"properties":{"name":{"title":"CustomProduct.name","description":"The main title of the product. E.g:\n`BAVARIA — 35 SPORTS CRUISER (2006)`","type":"string"},"description":{"title":"CustomProduct.description","description":"The description of the product. This shows right under the price so it is useful for subscriptions/rentals. E.g:\n\n`(per day)\\n\\nCome and have a fantastic sailing adventure aboard our boat. \\nShe is a Bavaria 35 sports cruiser and is powered by 2 economical Volvo D6’s with Bravo 2 outdrives as well as a bow thruster. This Makes maneuvering very easy. She can accommodate up to 8 people for day charters and for overnight charters she can accommodate 4 in comfort in 2 cabins.`","type":"string"},"priceAmount1000":{"title":"CustomProduct.priceAmount1000","description":"The price amount multiplied by 1000. For example, for something costing `825` units of currency:\n`825000`","type":"number"},"currency":{"title":"CustomProduct.currency","description":"The [**ISO 4217**](https://en.wikipedia.org/wiki/ISO_4217) 3 letter currency code. E.g (Swedish krona)\n`SEK`","type":"string"},"url":{"title":"CustomProduct.url","description":"The URL of the product.\n\nNOTE: At the moment, the URL DOES NOT WORK. It shows up for the recipient but they will not be able to click it. As a rememdy, it is added as a reply to the product message.","type":"string"}},"required":["name","description","priceAmount1000","currency"],"additionalProperties":false,"title":"CustomProduct","type":"object"},"CartItem":{"properties":{"id":{"title":"CartItem.id","description":"Product ID","type":"string"},"name":{"title":"CartItem.name","description":"Product name","type":"string"},"qty":{"title":"CartItem.qty","description":"Amount of this item in the cart","type":"number"},"thumbnailId":{"title":"CartItem.thumbnailId","type":"string"},"thumbnailUrl":{"title":"CartItem.thumbnailUrl","description":"URL to .enc file of the thumbnail. Just change the filetype to .jpg to view the thumbnail","type":"string"}},"required":["id","name","qty","thumbnailId","thumbnailUrl"],"additionalProperties":false,"title":"CartItem","type":"object"},"Product":{"properties":{"id":{"title":"Product.id","description":"Product ID","type":"string"},"isHidden":{"title":"Product.isHidden","description":"`true` if the product is hidden from public view.","type":"boolean"},"catalogWid":{"title":"Product.catalogWid","description":"The id of the catalog in which this product is located.","type":"string"},"url":{"title":"Product.url","description":"The URL of the product.","type":"string"},"name":{"title":"Product.name","description":"The name of the product.","type":"string"},"description":{"title":"Product.description","description":"The description of the product.","type":"string"},"availability":{"anyOf":[{"title":"Product.availability","type":"number"},{"title":"Product.availability","enum":["unknown"],"type":"string"}],"title":"Product.availability","description":"The availiable quantity of this product.","default":"\"unknown\"`"},"reviewStatus":{"enum":["NO_REVIEW","PENDING","REJECTED","APPROVED","OUTDATED"],"title":"Product.reviewStatus","description":"The review status of the product","type":"string"},"imageCdnUrl":{"title":"Product.imageCdnUrl","description":"The url of the main image of the product.\n\nNOTE: If downloading manually, the filetype must be changed to .jpg to view the image.","type":"string"},"imageCount":{"title":"Product.imageCount","description":"The number of images of the product.","type":"number"},"additionalImageCdnUrl":{"items":{"title":"Product.additionalImageCdnUrl.[]","type":"string"},"title":"Product.additionalImageCdnUrl","description":"Array of URLs of the other images of the product. Does not include the main image.","type":"array"},"priceAmount1000":{"title":"Product.priceAmount1000","description":"The price of the product in 1000 units.","type":"number"},"retailerId":{"title":"Product.retailerId","description":"The custom id of the product.","type":"string"},"t":{"title":"Product.t","description":"The timestamp when the product was created / 1000","type":"number"},"currency":{"title":"Product.currency","description":"The [**ISO 4217**](https://en.wikipedia.org/wiki/ISO_4217) 3 letter currency code. E.g (Swedish krona)\n`SEK`","type":"string"}},"required":["id","currency"],"additionalProperties":false,"title":"Product","type":"object"},"Order":{"properties":{"id":{"title":"Order.id","description":"Order ID","type":"string"},"createdAt":{"title":"Order.createdAt","description":"epoch ts divided by 1000","type":"number"},"currency":{"title":"Order.currency","description":"The [**ISO 4217**](https://en.wikipedia.org/wiki/ISO_4217) 3 letter currency code. E.g (Swedish krona)\n`SEK`","type":"string"},"products":{"items":{"$ref":"#/components/schemas/CartItem","title":"Order.products.[]"},"title":"Order.products","description":"An array of items in the cart","type":"array"},"sellerJid":{"title":"Order.sellerJid","type":"string"},"subtotal":{"title":"Order.subtotal"},"total":{"title":"Order.total"},"message":{"$ref":"#/components/schemas/Message","title":"Order.message","description":"The message object associated with the order. Only populated in `onOrder` callback."}},"required":["id","createdAt","currency","products","sellerJid","subtotal","total"],"additionalProperties":false,"title":"Order","type":"object"},"Reaction":{"properties":{"aggregateEmoji":{"title":"Reaction.aggregateEmoji","description":"The aggregate emoji used for the reaction.","type":"string"},"id":{"title":"Reaction.id","description":"The identifier of the reaction","type":"string"},"hasReactionByMe":{"title":"Reaction.hasReactionByMe","description":"If the reaction is also sent by the host account","type":"boolean"},"senders":{"items":{"$ref":"#/components/schemas/ReactionRecord","title":"Reaction.senders.[]"},"title":"Reaction.senders","description":"The senders of this spefcific reaction","type":"array"}},"required":["aggregateEmoji","id","hasReactionByMe","senders"],"additionalProperties":false,"title":"Reaction","description":"A reaction is identified the specific emoji.","type":"object"},"ReactionRecord":{"properties":{"ack":{"$ref":"#/components/schemas/MessageAck","title":"ReactionRecord.ack","description":"The acknowledgement of the reaction"},"id":{"title":"ReactionRecord.id","description":"The ID of the reaction","type":"string"},"msgKey":{"title":"ReactionRecord.msgKey","type":"string"},"parentMsgKey":{"title":"ReactionRecord.parentMsgKey","type":"string"},"orphan":{"title":"ReactionRecord.orphan","type":"number"},"reactionText":{"title":"ReactionRecord.reactionText","description":"The text of the reaction","type":"string"},"read":{"title":"ReactionRecord.read","description":"If the reaction has been read","type":"boolean"},"senderUserJid":{"$ref":"#/components/schemas/ContactId","title":"ReactionRecord.senderUserJid","description":"The ID of the reaction sender"},"timestamp":{"title":"ReactionRecord.timestamp","description":"The timestamp of the reaction","type":"number"}},"required":["ack","id","msgKey","parentMsgKey","orphan","reactionText","read","senderUserJid","timestamp"],"additionalProperties":false,"title":"ReactionRecord","description":"The specific reaction by a user","type":"object"},"ReactionEvent":{"properties":{"message":{"$ref":"#/components/schemas/Message","title":"ReactionEvent.message","description":"The message being reacted to"},"reactionByMe":{"$ref":"#/components/schemas/Reaction","title":"ReactionEvent.reactionByMe","description":"The reaction sent by the host account"},"reactions":{"items":{"$ref":"#/components/schemas/Reaction","title":"ReactionEvent.reactions.[]"},"title":"ReactionEvent.reactions","description":"An array of all reactions","type":"array"},"type":{"enum":["add","change"],"title":"ReactionEvent.type","description":"The type of the reaction event.","type":"string"}},"required":["message","reactionByMe","reactions","type"],"additionalProperties":false,"title":"ReactionEvent","description":"Emitted by onReaction","type":"object"},"SessionInfo":{"properties":{"WA_VERSION":{"title":"SessionInfo.WA_VERSION","type":"string"},"PAGE_UA":{"title":"SessionInfo.PAGE_UA","type":"string"},"WA_AUTOMATE_VERSION":{"title":"SessionInfo.WA_AUTOMATE_VERSION","type":"string"},"BROWSER_VERSION":{"title":"SessionInfo.BROWSER_VERSION","type":"string"},"LAUNCH_TIME_MS":{"title":"SessionInfo.LAUNCH_TIME_MS","type":"number"},"NUM":{"title":"SessionInfo.NUM","type":"string"},"OS":{"title":"SessionInfo.OS","type":"string"},"START_TS":{"title":"SessionInfo.START_TS","type":"number"},"PHONE_VERSION":{"title":"SessionInfo.PHONE_VERSION","type":"string"},"NUM_HASH":{"title":"SessionInfo.NUM_HASH","type":"string"},"PATCH_HASH":{"title":"SessionInfo.PATCH_HASH","type":"string"},"OW_KEY":{"title":"SessionInfo.OW_KEY","type":"string"},"INSTANCE_ID":{"title":"SessionInfo.INSTANCE_ID","type":"string"},"RAM_INFO":{"title":"SessionInfo.RAM_INFO","type":"string"},"PPTR_VERSION":{"title":"SessionInfo.PPTR_VERSION","type":"string"},"LATEST_VERSION":{"title":"SessionInfo.LATEST_VERSION","type":"boolean"},"CLI":{"title":"SessionInfo.CLI","type":"boolean"},"ACC_TYPE":{"enum":["PERSONAL","BUSINESS"],"title":"SessionInfo.ACC_TYPE","type":"string"}},"required":["WA_VERSION","PAGE_UA","WA_AUTOMATE_VERSION","BROWSER_VERSION"],"additionalProperties":false,"title":"SessionInfo","type":"object"},"HealthCheck":{"properties":{"queuedMessages":{"title":"HealthCheck.queuedMessages","description":"The number of messages queued up in the browser. Messages can start being queued up due to the web app awaiting a connection with the host device.\n\nHealthy: 0","type":"number"},"state":{"$ref":"#/components/schemas/STATE","title":"HealthCheck.state","description":"The state of the web app.\n\nHealthy: 'CONNECTED'"},"isPhoneDisconnected":{"title":"HealthCheck.isPhoneDisconnected","description":"Whether or not the \"Phone is disconnected\" message is showing within the web app.\n\nHealthy: `false`","type":"boolean"},"isHere":{"title":"HealthCheck.isHere","description":"Returns `true` if \"Use Here\" button is not detected\n\nHealthy: `true`","type":"boolean"},"wapiInjected":{"title":"HealthCheck.wapiInjected","description":"Returns `true` if the `WAPI` object is detected.\n\nHealthy: `true`","type":"boolean"},"online":{"title":"HealthCheck.online","description":"Result of `window.navigator.onLine`\n\nHealthy: `true`","type":"boolean"},"tryingToReachPhone":{"title":"HealthCheck.tryingToReachPhone","description":"Returns `true` if \"trying to reach phone\" dialog is detected\n\nHealthy: `false`","type":"boolean"},"retryingIn":{"title":"HealthCheck.retryingIn","description":"Returns the number of seconds the \"Retrying in ...\" dialog is indicating. If the dialog is not showing, it will return `0`.\n\nHealthy: `0`","type":"number"},"batteryLow":{"title":"HealthCheck.batteryLow","description":"Returns `true` if \"Phone battery low\" message is detected\n\nHealthy: `false`","type":"boolean"}},"additionalProperties":false,"title":"HealthCheck","type":"object"},"expressMiddleware":{"title":"expressMiddleware"},"ChatwootConfig":{"properties":{"chatwootUrl":{"title":"ChatwootConfig.chatwootUrl","description":"The URL of the chatwoot inbox. If you want this integration to create & manage the inbox for you, you can omit the inbox part.","type":"string"},"chatwootApiAccessToken":{"title":"ChatwootConfig.chatwootApiAccessToken","description":"The API access token which you can get from your account menu.","type":"string"},"apiHost":{"title":"ChatwootConfig.apiHost","description":"The API host which will be used as the webhook address in the Chatwoot inbox.","type":"string"},"host":{"title":"ChatwootConfig.host","description":"Similar to apiHost","type":"string"},"https":{"title":"ChatwootConfig.https","description":"Whether or not to use https for the webhook address","type":"boolean"},"cert":{"title":"ChatwootConfig.cert","description":"The certificate for https","type":"string"},"privkey":{"title":"ChatwootConfig.privkey","description":"The private key for https","type":"string"},"key":{"title":"ChatwootConfig.key","description":"The API key used to secure the instance webhook address","type":"string"},"forceUpdateCwWebhook":{"title":"ChatwootConfig.forceUpdateCwWebhook","description":"Whether or not to update the webhook address in the Chatwoot inbox on launch","type":"boolean"},"port":{"title":"ChatwootConfig.port","description":"port","type":"number"}},"required":["chatwootUrl","chatwootApiAccessToken","apiHost","host","cert","privkey","port"],"additionalProperties":false,"title":"ChatwootConfig","type":"object"},"cliFlags":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"}]},"title":"cliFlags","type":"object"},"ConfigLogTransport":{"properties":{"type":{"enum":["syslog","console","file","ev"],"title":"ConfigLogTransport.type","description":"The type of winston transport. At the moment only `file`, `console`, `ev` and `syslog` are supported.","type":"string"},"options":{"title":"ConfigLogTransport.options","description":"The options for the transport. Generally only required for syslog but you can use this to override default options for other types of transports."},"done":{"title":"ConfigLogTransport.done","description":"If the transport has already been added to the logger. The logging set up command handles this for you.","type":"boolean"}},"required":["type"],"additionalProperties":false,"title":"ConfigLogTransport","type":"object"},"CollectorOptions":{"properties":{"maxProcessed":{"title":"CollectorOptions.maxProcessed","description":"The maximum amount of items to process","type":"number"},"max":{"title":"CollectorOptions.max","description":"The maximum amount of items to collect","type":"number"},"time":{"title":"CollectorOptions.time","description":"Max time to wait for items in milliseconds","type":"number"},"idle":{"title":"CollectorOptions.idle","description":"Max time allowed idle","type":"number"},"dispose":{"title":"CollectorOptions.dispose","description":"Whether to dispose data when it's deleted","type":"boolean"}},"additionalProperties":false,"title":"CollectorOptions","description":"Options to be applied to the collector.","type":"object"},"AwaitMessagesOptions":{"properties":{"errors":{"items":{"title":"AwaitMessagesOptions.errors.[]","type":"string"},"title":"AwaitMessagesOptions.errors","description":"An array of \"reasons\" that would result in the awaitMessages command to throw an error.","type":"array"}},"additionalProperties":false,"title":"AwaitMessagesOptions","type":"object"},"CurrentDialogProps":{"additionalProperties":{},"title":"CurrentDialogProps","type":"object"},"DialogState":{"properties":{"currentStep":{"title":"DialogState.currentStep","type":"number"},"currentProps":{"title":"DialogState.currentProps"},"lastInput":{"title":"DialogState.lastInput"},"isComplete":{"title":"DialogState.isComplete","type":"boolean"},"isError":{"title":"DialogState.isError","type":"boolean"},"errorMessage":{"title":"DialogState.errorMessage","type":"string"}},"required":["currentStep","currentProps","lastInput","isComplete","isError","errorMessage"],"additionalProperties":false,"title":"DialogState","type":"object"},"DialogTemplate":{"properties":{"\"dialogId\"":{"title":"DialogTemplate.\"dialogId\"","type":"string"},"\"privateOnly\"":{"title":"DialogTemplate.\"privateOnly\"","type":"boolean"},"\"identifier\"":{"title":"DialogTemplate.\"identifier\"","type":"string"},"\"successMessage\"":{"title":"DialogTemplate.\"successMessage\"","type":"string"},"\"startMessage\"":{"title":"DialogTemplate.\"startMessage\"","type":"string"},"\"properties\"":{"additionalProperties":{"$ref":"#/components/schemas/DialogProperty"},"title":"DialogTemplate.\"properties\"","type":"object"}},"required":["\"dialogId\"","\"privateOnly\"","\"identifier\"","\"successMessage\"","\"startMessage\"","\"properties\""],"additionalProperties":false,"title":"DialogTemplate","type":"object"},"CheckFunction":{"title":"CheckFunction"},"DialogProperty":{"properties":{"\"order\"":{"title":"DialogProperty.\"order\"","type":"number"},"\"key\"":{"title":"DialogProperty.\"key\"","type":"string"},"\"prompt\"":{"title":"DialogProperty.\"prompt\"","type":"string"},"\"type\"":{"title":"DialogProperty.\"type\"","type":"string"},"\"skipCondition\"":{"$ref":"#/components/schemas/CheckFunction","title":"DialogProperty.\"skipCondition\""},"\"options\"":{"anyOf":[{"items":{"$ref":"#/components/schemas/DialogButtons","title":"DialogProperty.\"options\".[]"},"title":"DialogProperty.\"options\".[]","type":"array"},{"items":{"$ref":"#/components/schemas/DialogListMessageSection","title":"DialogProperty.\"options\".[]"},"title":"DialogProperty.\"options\".[]","type":"array"}],"title":"DialogProperty.\"options\""},"\"validation\"":{"items":{"$ref":"#/components/schemas/DialogValidation","title":"DialogProperty.\"validation\".[]"},"title":"DialogProperty.\"validation\"","type":"array"}},"required":["\"order\"","\"key\"","\"prompt\"","\"type\"","\"validation\""],"additionalProperties":false,"title":"DialogProperty","type":"object"},"DialogButtons":{"properties":{"label":{"title":"DialogButtons.label","type":"string"},"value":{"title":"DialogButtons.value","type":"string"}},"required":["label","value"],"additionalProperties":false,"title":"DialogButtons","type":"object"},"DialogListMessageSection":{"properties":{"title":{"title":"DialogListMessageSection.title","type":"string"},"rows":{"items":{"$ref":"#/components/schemas/DialogListMessageRow","title":"DialogListMessageSection.rows.[]"},"title":"DialogListMessageSection.rows","type":"array"}},"required":["title","rows"],"additionalProperties":false,"title":"DialogListMessageSection","type":"object"},"DialogListMessageRow":{"properties":{"title":{"title":"DialogListMessageRow.title","type":"string"},"description":{"title":"DialogListMessageRow.description","type":"string"},"value":{"title":"DialogListMessageRow.value","type":"string"}},"required":["title","description","value"],"additionalProperties":false,"title":"DialogListMessageRow","type":"object"},"DialogValidation":{"properties":{"\"type\"":{"$ref":"#/components/schemas/ValidationType","title":"DialogValidation.\"type\""},"\"value\"":{"anyOf":[{"title":"DialogValidation.\"value\"","type":"string"},{"$ref":"#/components/schemas/CheckFunction","title":"DialogValidation.\"value\""}],"title":"DialogValidation.\"value\""},"\"errorMessage\"":{"title":"DialogValidation.\"errorMessage\"","type":"string"}},"required":["\"type\"","\"value\"","\"errorMessage\""],"additionalProperties":false,"title":"DialogValidation","type":"object"},"MessagePreProcessor":{"title":"MessagePreProcessor","description":"A function that takes a message and returns a message.\n@param The message to be processed\n@param The client that received the message\n@param Whether the message has already been processed by another preprocessor. (This is useful in cases where you want to mutate the message for both onMessage and onAnyMessage events but only want to do the actual process, like uploading to s3, once.)\n@param The source of the message. This is useful for knowing if the message is from onMessage or onAnyMessage. Only processing one source will prevent duplicate processing."},"MPConfigType":{"anyOf":[{"$ref":"#/components/schemas/PREPROCESSORS","title":"MPConfigType"},{"$ref":"#/components/schemas/MessagePreProcessor","title":"MPConfigType"},{"items":{"anyOf":[{"$ref":"#/components/schemas/PREPROCESSORS","title":"MPConfigType.[]"},{"$ref":"#/components/schemas/MessagePreProcessor","title":"MPConfigType.[]"}],"title":"MPConfigType.[]"},"title":"MPConfigType.[]","type":"array"}],"title":"MPConfigType","description":"The actual type for [config.messagePreprocessor](/docs/api/interfaces/api_model_config.ConfigObject#messagepreprocessor)"}}
+{"CreateSessionRequest":{"properties":{"sessionId":{"title":"CreateSessionRequest.sessionId","type":"string"},"config":{"title":"CreateSessionRequest.config"},"waitForQR":{"title":"CreateSessionRequest.waitForQR","type":"boolean"}},"required":["sessionId"],"additionalProperties":false,"title":"CreateSessionRequest","type":"object"},"SessionResponse":{"properties":{"sessionId":{"title":"SessionResponse.sessionId","type":"string"},"status":{"title":"SessionResponse.status","type":"string"},"createdAt":{"title":"SessionResponse.createdAt","type":"string"},"lastActivity":{"title":"SessionResponse.lastActivity","type":"string"},"qrCode":{"title":"SessionResponse.qrCode","type":"string"},"message":{"title":"SessionResponse.message","type":"string"}},"required":["sessionId","status","createdAt","lastActivity"],"additionalProperties":false,"title":"SessionResponse","type":"object"},"ChatServer":{"title":"ChatServer","description":"The suffix used to identify a non-group chat id","enum":["c.us"],"type":"string"},"GroupChatServer":{"title":"GroupChatServer","description":"The suffix used to identify a group chat id","enum":["g.us"],"type":"string"},"WaServers":{"anyOf":[{"$ref":"#/components/schemas/ChatServer","title":"WaServers"},{"$ref":"#/components/schemas/GroupChatServer","title":"WaServers"}],"title":"WaServers","description":"A type alias for all available \"servers\""},"CountryCode":{"enum":[1,7,20,27,30,31,32,33,34,36,39,40,41,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58,60,61,62,63,64,65,66,81,82,84,86,90,91,92,93,94,95,98,211,212,213,216,218,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,248,249,250,251,252,253,254,255,256,257,258,260,261,262,263,264,265,266,267,268,269,290,291,297,298,299,350,351,352,353,354,355,356,357,358,359,370,371,372,373,374,375,376,377,378,380,381,382,383,385,386,387,389,420,421,423,500,501,502,503,504,505,506,507,508,509,590,591,592,593,594,595,596,597,598,599,670,672,673,674,675,676,677,678,679,680,681,682,683,685,686,687,688,689,690,691,692,850,852,853,855,856,880,886,960,961,962,963,964,965,966,967,968,970,971,972,973,974,975,976,977,992,993,994,995,996,998],"title":"CountryCode","description":"Type alias representing all available country codes","type":"number"},"AccountNumber":{"title":"AccountNumber","description":"The account number. It is made up of a country code and then the local number without the preceeding 0. For example, if a UK (+44) wa account is linked to the number 07123456789 then the account number will be 447123456789."},"GroupId":{"title":"GroupId","description":"A new group or community has the format of a random number followed by `@g.us`"},"GroupChatId":{"$ref":"#/components/schemas/GroupId","title":"GroupChatId","description":"A group chat ends with `@g.us` and usually has two parts, the timestamp of when it was created, and the user id of the number that created the group. For example `[creator number]-[timestamp]@g.us`\n\nExample:\n\n`\"447123456789-1445627445@g.us\"`"},"ContactId":{"title":"ContactId","description":"A contact id ends with `@c.us` and only contains the number of the contact. For example, if the country code of a contact is `44` and their number is `7123456789` then the contact id would be `447123456789@c.us`\n\nExample:\n\n`\"447123456789@c.us\"`"},"ChatId":{"anyOf":[{"$ref":"#/components/schemas/ContactId","title":"ChatId"},{"$ref":"#/components/schemas/GroupChatId","title":"ChatId"}],"title":"ChatId","description":"A chat id ends with `@c.us` or `@g.us` for group chats.\n\nExample:\n\nA group chat: `\"447123456789-1445627445@g.us\"`\nA group chat: `\"447123456789@g.us\"`"},"MessageId":{"title":"MessageId","description":"The id of a message. The format is `[boolean]_[ChatId]_[random character string]`\n\nExample:\n\n`\"false_447123456789@c.us_9C4D0965EA5C09D591334AB6BDB07FEB\"`"},"Content":{"title":"Content","description":"This is a generic type alias for the content of a message\n\nExample:\n\n`\"hello!\"`"},"NonSerializedId":{"properties":{"server":{"$ref":"#/components/schemas/WaServers","title":"NonSerializedId.server"},"user":{"$ref":"#/components/schemas/AccountNumber","title":"NonSerializedId.user"},"_serialized":{"$ref":"#/components/schemas/ContactId","title":"NonSerializedId._serialized"}},"required":["server","user","_serialized"],"additionalProperties":false,"title":"NonSerializedId","type":"object"},"DataURL":{"title":"DataURL","description":"Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents. They were formerly known as \"data URIs\" until that name was retired by the WHATWG.\n\n\nData URLs are composed of four parts: a prefix (data:), a MIME type indicating the type of data, an optional base64 token if non-textual, and the data itself:\n\nExample:\n`\"data:[][;base64],\"`\n\nLearn more here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs"},"Base64":{"title":"Base64","description":"Base64 is basically a file encoded as a string.\n\nBase64 is a group of similar binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation. The term Base64 originates from a specific MIME content transfer encoding.\n\nLearn more here: https://developer.mozilla.org/en-US/docs/Glossary/Base64"},"FilePath":{"title":"FilePath","description":"The relative or absolute path of a file\n\nLearn more here: https://www.w3schools.com/html/html_filepaths.asp"},"GetURL":{"title":"GetURL","description":"A URL of a file used with a GET request"},"AdvancedFile":{"anyOf":[{"$ref":"#/components/schemas/DataURL","title":"AdvancedFile"},{"$ref":"#/components/schemas/FilePath","title":"AdvancedFile"},{"$ref":"#/components/schemas/GetURL","title":"AdvancedFile"}],"title":"AdvancedFile","description":"Some file based actions in open-wa are powerful enough to take a dataurl, url or filepath"},"Button":{"properties":{"id":{"title":"Button.id","type":"string"},"text":{"title":"Button.text","type":"string"}},"required":["id","text"],"additionalProperties":false,"title":"Button","type":"object"},"AdvancedButton":{"properties":{"id":{"title":"AdvancedButton.id","type":"string"},"text":{"title":"AdvancedButton.text","type":"string"},"url":{"title":"AdvancedButton.url","type":"string"},"number":{"title":"AdvancedButton.number","type":"string"}},"required":["text"],"additionalProperties":false,"title":"AdvancedButton","type":"object"},"Row":{"properties":{"title":{"title":"Row.title","type":"string"},"description":{"title":"Row.description","type":"string"},"rowId":{"title":"Row.rowId","type":"string"}},"required":["title","description","rowId"],"additionalProperties":false,"title":"Row","type":"object"},"Section":{"properties":{"title":{"title":"Section.title","type":"string"},"rows":{"items":{"$ref":"#/components/schemas/Row","title":"Section.rows.[]"},"title":"Section.rows","type":"array"}},"required":["title","rows"],"additionalProperties":false,"title":"Section","type":"object"},"LocationButtonBody":{"properties":{"lat":{"title":"LocationButtonBody.lat","type":"number"},"lng":{"title":"LocationButtonBody.lng","type":"number"},"caption":{"title":"LocationButtonBody.caption","type":"string"}},"required":["lat","lng","caption"],"additionalProperties":false,"title":"LocationButtonBody","type":"object"},"Call":{"properties":{"id":{"title":"Call.id","description":"The id of the call","type":"string"},"peerJid":{"$ref":"#/components/schemas/ContactId","title":"Call.peerJid","description":"The id of the account calling"},"offerTime":{"title":"Call.offerTime","description":"The epoch timestamp of the call. You will have to multiply this by 1000 to get the actual epoch timestamp","type":"number"},"isVideo":{"title":"Call.isVideo","description":"Whether or not the call is a video call","type":"boolean"},"isGroup":{"title":"Call.isGroup","description":"Whether or not the call is a group call","type":"boolean"},"canHandleLocally":{"title":"Call.canHandleLocally","type":"boolean"},"outgoing":{"title":"Call.outgoing","description":"The direction of the call.","type":"boolean"},"webClientShouldHandle":{"title":"Call.webClientShouldHandle","type":"boolean"},"participants":{"items":{"$ref":"#/components/schemas/ContactId","title":"Call.participants.[]"},"title":"Call.participants","description":"The other participants on a group call","type":"array"},"State":{"$ref":"#/components/schemas/CallState","title":"Call.State","description":"State of the call"}},"required":["id","peerJid","offerTime","isVideo","isGroup","canHandleLocally","outgoing","webClientShouldHandle","participants","State"],"additionalProperties":false,"title":"Call","type":"object"},"BaseChat":{"properties":{"archive":{"title":"BaseChat.archive","type":"boolean"},"changeNumberNewJid":{"title":"BaseChat.changeNumberNewJid"},"changeNumberOldJid":{"title":"BaseChat.changeNumberOldJid"},"contact":{"$ref":"#/components/schemas/Contact","title":"BaseChat.contact","description":"The contact related to this chat"},"groupMetadata":{"$ref":"#/components/schemas/GroupMetadata","title":"BaseChat.groupMetadata","description":"Group metadata for this chat"},"isAnnounceGrpRestrict":{"title":"BaseChat.isAnnounceGrpRestrict","description":"If the chat is a group chat is restricted"},"formattedTitle":{"title":"BaseChat.formattedTitle","description":"The title of the chat","type":"string"},"canSend":{"title":"BaseChat.canSend","description":"Whether your host account is able to send messages to this chat","type":"boolean"},"isReadOnly":{"title":"BaseChat.isReadOnly","description":"Whether the chat is a group chat and the group is restricted","type":"boolean"},"kind":{"title":"BaseChat.kind","type":"string"},"labels":{"title":"BaseChat.labels","description":"The labels attached to this chat."},"lastReceivedKey":{"title":"BaseChat.lastReceivedKey","description":"The ID of the last message received in this chat"},"modifyTag":{"title":"BaseChat.modifyTag","type":"number"},"msgs":{"title":"BaseChat.msgs","description":"The messages in the chat"},"muteExpiration":{"title":"BaseChat.muteExpiration","description":"The expiration timestamp of the chat mute","type":"number"},"name":{"title":"BaseChat.name","description":"The name of the chat","type":"string"},"notSpam":{"title":"BaseChat.notSpam","description":"Whether the chat is marked as spam","type":"boolean"},"pendingMsgs":{"title":"BaseChat.pendingMsgs","description":"Messages that are pending to be sent","type":"boolean"},"pin":{"title":"BaseChat.pin","description":"Whether the chat is pinned","type":"number"},"presence":{"title":"BaseChat.presence","description":"The presence state of the chat participant"},"t":{"title":"BaseChat.t","description":"The timestamp of the last interaction in the chat","type":"number"},"unreadCount":{"title":"BaseChat.unreadCount","description":"The number of undread messages in this chat","type":"number"},"ack":{"title":"BaseChat.ack"},"isOnline":{"title":"BaseChat.isOnline","description":"@deprecated This is unreliable. Use the method [`isChatOnline`](https://open-wa.github.io/wa-automate-nodejs/classes/client.html#ischatonline) instead."},"lastSeen":{"title":"BaseChat.lastSeen","description":"@deprecated This is unreliable. Use the method [`getLastSeen`](https://open-wa.github.io/wa-automate-nodejs/classes/client.html#getlastseen) instead."},"pic":{"title":"BaseChat.pic","description":"URL of the chat picture if available","type":"string"}},"required":["archive","changeNumberNewJid","changeNumberOldJid","contact","groupMetadata","isAnnounceGrpRestrict","isReadOnly","kind","labels","lastReceivedKey","modifyTag","msgs","muteExpiration","name","notSpam","pendingMsgs","pin","presence","t","unreadCount"],"additionalProperties":false,"title":"BaseChat","type":"object"},"SingleChat":{"properties":{"id":{"$ref":"#/components/schemas/ContactId","title":"SingleChat.id","description":"The id of the chat"},"isGroup":{"title":"SingleChat.isGroup","description":"Whether the chat is a group chat","enum":[false],"type":"boolean"}},"required":["id","isGroup"],"additionalProperties":false,"title":"SingleChat","type":"object"},"GroupChat":{"properties":{"id":{"$ref":"#/components/schemas/GroupChatId","title":"GroupChat.id","description":"The id of the chat"},"isGroup":{"title":"GroupChat.isGroup","description":"Whether the chat is a group chat","enum":[true],"type":"boolean"},"groupType":{"enum":["DEFAULT","COMMUNITY","LINKED_ANNOUNCEMENT_GROUP","LINKED_GENERAL_GROUP","LINKED_SUBGROUP"],"title":"GroupChat.groupType","description":"The type of the group","type":"string"}},"required":["id","isGroup","groupType"],"additionalProperties":false,"title":"GroupChat","type":"object"},"Chat":{"anyOf":[{"$ref":"#/components/schemas/SingleChat","title":"Chat"},{"$ref":"#/components/schemas/GroupChat","title":"Chat"}],"title":"Chat"},"LiveLocationChangedEvent":{"properties":{"id":{"title":"LiveLocationChangedEvent.id","type":"string"},"lat":{"title":"LiveLocationChangedEvent.lat","type":"number"},"lng":{"title":"LiveLocationChangedEvent.lng","type":"number"},"speed":{"title":"LiveLocationChangedEvent.speed","type":"number"},"lastUpdated":{"title":"LiveLocationChangedEvent.lastUpdated","type":"number"},"accuracy":{"title":"LiveLocationChangedEvent.accuracy","type":"number"},"degrees":{"title":"LiveLocationChangedEvent.degrees"},"msgId":{"title":"LiveLocationChangedEvent.msgId","description":"The message id that was sent when the liveLocation session was started.","type":"string"}},"required":["id","lat","lng","speed","lastUpdated","accuracy","degrees"],"additionalProperties":false,"title":"LiveLocationChangedEvent","type":"object"},"GroupChatCreationParticipantAddResponse":{"properties":{"code":{"enum":[200,400,403],"title":"GroupChatCreationParticipantAddResponse.code","description":"The resultant status code for adding the participant.\n\n200 if the participant was added successfully during the creation of the group.\n\n403 if the participant does not allow their account to be added to group chats. If you receive a 403, you will also get an `invite_code` and `invite_code_exp`","type":"number"},"invite_code":{"title":"GroupChatCreationParticipantAddResponse.invite_code","description":"If the participant is not allowed to be added to group chats due to their privacy settings, you will receive an `invite_code` which you can send to them via a text.","type":"string"},"invite_code_exp":{"title":"GroupChatCreationParticipantAddResponse.invite_code_exp","description":"The expiry ts of the invite_code. It is a number wrapped in a string, in order to get the proper time you can use this:\n\n```javascript\n new Date(Number(invite_code_exp)*1000)\n```","type":"string"}},"required":["code"],"additionalProperties":false,"title":"GroupChatCreationParticipantAddResponse","type":"object"},"GroupChatCreationResponse":{"properties":{"status":{"enum":[200,400],"title":"GroupChatCreationResponse.status","description":"The resultant status code of the group chat creation.\n\n200 if the group was created successfully.\n\n400 if the initial participant does not exist","type":"number"},"gid":{"$ref":"#/components/schemas/GroupChatId","title":"GroupChatCreationResponse.gid","description":"The group chat id"},"participants":{"items":{"properties":{"ContactId":{"$ref":"#/components/schemas/GroupChatCreationParticipantAddResponse","title":"GroupChatCreationResponse.participants.[].ContactId"}},"additionalProperties":false,"title":"GroupChatCreationResponse.participants.[]","type":"object"},"title":"GroupChatCreationResponse.participants","description":"The initial requested participants and their corresponding add responses","type":"array"}},"required":["status","gid","participants"],"additionalProperties":false,"title":"GroupChatCreationResponse","type":"object"},"EphemeralDuration":{"enum":[86400,604800,7776000],"title":"EphemeralDuration","description":"Ephemeral duration can be 1 day, 7 days or 90 days. The default is 1 day.","type":"number"},"SessionData":{"properties":{"WABrowserId":{"title":"SessionData.WABrowserId","type":"string"},"WASecretBundle":{"title":"SessionData.WASecretBundle","type":"string"},"WAToken1":{"title":"SessionData.WAToken1","type":"string"},"WAToken2":{"title":"SessionData.WAToken2","type":"string"}},"additionalProperties":false,"title":"SessionData","type":"object"},"DevTools":{"properties":{"user":{"title":"DevTools.user","description":"Username for devtools","type":"string"},"pass":{"title":"DevTools.pass","description":"Password for devtools","type":"string"}},"required":["user","pass"],"additionalProperties":false,"title":"DevTools","type":"object"},"EventPayload":{"properties":{"ts":{"title":"EventPayload.ts","type":"number"},"sessionId":{"title":"EventPayload.sessionId","type":"string"},"id":{"title":"EventPayload.id","type":"string"},"event":{"$ref":"#/components/schemas/SimpleListener","title":"EventPayload.event"},"data":{"title":"EventPayload.data"}},"required":["ts","sessionId","id","event","data"],"additionalProperties":{},"title":"EventPayload","type":"object"},"Webhook":{"properties":{"url":{"title":"Webhook.url","description":"The endpoint to send (POST) the event to.","type":"string"},"requestConfig":{"$ref":"#/components/schemas/AxiosRequestConfig","title":"Webhook.requestConfig","description":"The optional AxiosRequestConfig to use for firing the webhook event. This can be useful if you want to add some authentication when POSTing data to your server.\n\nFor example, if your webhook requires the username `admin` and password `1234` for authentication, you can set the requestConfig to:\n```\n{\n auth: {\n username: \"admin\",\n password: \"1234\",\n }\n}\n```\n\nPlease note, for security reasons, this is not returned when listing webhooks however it is returned when registering a webhook for verification purposes."},"id":{"title":"Webhook.id","description":"The ID of the given webhook setup. Use this ID with [[removeWebhook]]","type":"string"},"events":{"items":{"$ref":"#/components/schemas/SimpleListener","title":"Webhook.events.[]"},"title":"Webhook.events","description":"An array of events that are registered to be sent to this webhook.","type":"array"},"ts":{"title":"Webhook.ts","description":"Time when the webhook was registered in epoch time","type":"number"}},"required":["url","id","events","ts"],"additionalProperties":false,"title":"Webhook","type":"object"},"ProxyServerCredentials":{"properties":{"protocol":{"title":"ProxyServerCredentials.protocol","description":"The protocol on which the proxy is running. E.g `http`, `https`, `socks4` or `socks5`. This is optional and can be automatically determined from the address.","type":"string"},"address":{"title":"ProxyServerCredentials.address","description":"Proxy Server address. This can include the port e.g '127.0.0.1:5005'","type":"string"},"username":{"title":"ProxyServerCredentials.username","description":"Username for Proxy Server authentication","type":"string"},"password":{"title":"ProxyServerCredentials.password","description":"Password for Proxy Server authentication","type":"string"}},"required":["address","username","password"],"additionalProperties":false,"title":"ProxyServerCredentials","type":"object"},"ConfigObject":{"properties":{"sessionData":{"anyOf":[{"$ref":"#/components/schemas/SessionData","title":"ConfigObject.sessionData"},{"$ref":"#/components/schemas/Base64","title":"ConfigObject.sessionData"}],"title":"ConfigObject.sessionData","description":"@deprecated The authentication object (as a JSON object or a base64 encoded string) that is required to migrate a session from one instance to another or to just restart an existing instance.\nThis sessionData is provided in a generated JSON file (it's a json file but contains the JSON data as a base64 encoded string) upon QR scan or an event.\n\nYou can capture the event like so:\n```javascript\nimport {create, ev} from '@open-wa/wa-automate';\n\n ev.on('sessionData.**', async (sessionData, sessionId) =>{\n console.log(sessionId, sessionData)\n })\n\n//or as base64 encoded string\n\n ev.on('sessionDataBase64.**', async (sessionDatastring, sessionId) =>{\n console.log(sessionId, sessionDatastring)\n })\n```\n NOTE: You can set sessionData as an evironmental variable also! The variable name has to be [sessionId (default = 'session) in all caps]_DATA_JSON. You have to make sure to surround your session data with single quotes to maintain the formatting.\n\nFor example:\n\nsessionId = 'session'\n\nTo set env var:\n```bash\n export SESSION_DATA_JSON=`...`\n```\nwhere ... is copied from session.data.json this will be a string most likley starting in `ey...` and ending with `==`\n\nSetting the sessionData in the environmental variable will override the sessionData object in the config."},"linkCode":{"title":"ConfigObject.linkCode","description":"There is a new way to login to your host account by entering a link code after a confirmation step from the host account device. In order to use this feature you MUST set the host account number as a string or number beforehand as a property of the config object.\n\ne.g\n```\nlinkCode: '1234567890'\n```","type":"string"},"browserWSEndpoint":{"title":"ConfigObject.browserWSEndpoint","description":"ALPHA EXPERIMENTAL FEATURE! DO NOT USE IN PRODUCTION, REQUIRES TESTING.\n\nLearn more:\n\nhttps://pptr.dev/#?product=Puppeteer&version=v3.1.0&show=api-puppeteerconnectoptions\n\nhttps://medium.com/@jaredpotter1/connecting-puppeteer-to-existing-chrome-window-8a10828149e0","type":"string"},"useStealth":{"title":"ConfigObject.useStealth","description":"This flag allows you to disable or enable the use of the puppeteer stealth plugin. It is a good idea to use it, however it can cause issues sometimes. Set this to false if you are experiencing `browser.setMaxListeneres` issue. For now the default for this is false.","default":"`false`","type":"boolean"},"sessionDataPath":{"title":"ConfigObject.sessionDataPath","description":"The path relative to the current working directory (i.e where you run the command to start your process). This will be used to store and read your `.data.json` files. defualt to ''","type":"string"},"bypassCSP":{"title":"ConfigObject.bypassCSP","description":"Disable cors see: https://pptr.dev/#?product=Puppeteer&version=v3.0.4&show=api-pagesetbypasscspenabled If you are having an issue with sending media try to set this to true. Otherwise leave it set to false.","default":"`false`","type":"boolean"},"chromiumArgs":{"items":{"title":"ConfigObject.chromiumArgs.[]","type":"string"},"title":"ConfigObject.chromiumArgs","description":"This allows you to pass any array of custom chrome/chromium argument strings to the puppeteer instance.\nYou can find all possible arguements [here](https://peter.sh/experiments/chromium-command-line-switches/).","type":"array"},"skipBrokenMethodsCheck":{"title":"ConfigObject.skipBrokenMethodsCheck","description":"If set to true, skipBrokenMethodsCheck will bypass the health check before startup. It is highly suggested to not set this to true.","default":"`false`","type":"boolean"},"skipUpdateCheck":{"title":"ConfigObject.skipUpdateCheck","description":"If set to true, `skipUpdateCheck` will bypass the latest version check. This saves some time on boot (around 150 ms).","default":"`false`","type":"boolean"},"sessionId":{"title":"ConfigObject.sessionId","description":"This is the name of the session. You have to make sure that this is unique for every session.","default":"`session`","type":"string"},"licenseKey":{"$ref":"#/components/schemas/LicenseKey","title":"ConfigObject.licenseKey","description":"In order to unlock the functionality to send texts to unknown numbers, you need a License key.\nOne License Key is valid for each number. Each License Key starts from £5 per month.\n\nPlease check README for instructions on how to get a license key.\n\nNotes:\n1. You can change the number assigned to that License Key at any time, just message me the new number on the private discord channel.\n2. In order to cancel your License Key, simply stop your membership."},"customUserAgent":{"title":"ConfigObject.customUserAgent","description":"You may set a custom user agent. However, due to recent developments, this is not really neccessary any more.","type":"string"},"devtools":{"anyOf":[{"title":"ConfigObject.devtools","type":"boolean"},{"$ref":"#/components/schemas/DevTools","title":"ConfigObject.devtools"}],"title":"ConfigObject.devtools","description":"You can enable remote devtools by setting this to trye. If you set this to true there will be security on the devtools url.\nIf you want, you can also pass a username & password."},"blockCrashLogs":{"title":"ConfigObject.blockCrashLogs","description":"Setting this to true will block any network calls to crash log servers. This should keep anything you do under the radar.","default":"`true`","type":"boolean"},"cacheEnabled":{"title":"ConfigObject.cacheEnabled","description":"Setting this to false turn off the cache. This may improve memory usage.","default":"`false`","type":"boolean"},"browserRevision":{"title":"ConfigObject.browserRevision","description":"This is the specific browser revision to be downlaoded and used. You can find browser revision strings here: http://omahaproxy.appspot.com/\nLearn more about it here: https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#class-browserfetcher\nIf you're having trouble with sending images, try '737027'.\nIf you go too far back things will start breaking !!!!!!\nNOTE: THIS WILL OVERRIDE useChrome and executablePath. ONLY USE THIS IF YOU KNOW WHAT YOU ARE DOING.","type":"string"},"throwErrorOnTosBlock":{"title":"ConfigObject.throwErrorOnTosBlock","description":"Setting this to true will throw an error if a session is not able to get a QR code or is unable to restart a session.","type":"boolean"},"headless":{"title":"ConfigObject.headless","description":"By default, all instances of @open-wa/wa-automate are headless (i.e you don't see a chrome window open), you can set this to false to show the chrome/chromium window.","default":"`true`","type":"boolean"},"autoRefresh":{"title":"ConfigObject.autoRefresh","description":"@deprecated THIS IS LOCKED TO `true` AND CANNOT BE TURNED OFF. PLEASE SEE [[authTimeout]]\n\nSetting this to true will result in new QR codes being generated if the end user takes too long to scan the QR code.","default":"`true`","type":"boolean"},"qrRefreshS":{"title":"ConfigObject.qrRefreshS","description":"@deprecated This now has no effect\n\nThis determines the interval at which to refresh the QR code. By default, WA updates the qr code every 18-19 seconds so make sure this value is set to UNDER 18 seconds!!","type":"number"},"qrTimeout":{"title":"ConfigObject.qrTimeout","description":"This determines how long the process should wait for a QR code to be scanned before killing the process entirely. To have the system wait continuously, set this to `0`.","default":"60","type":"number"},"waitForRipeSessionTimeout":{"title":"ConfigObject.waitForRipeSessionTimeout","description":"This determines how long the process should wait for a session to load fully before continuing the launch process.\nSet this to 0 to wait forever. Default is 5 seconds.","default":"5","type":"number"},"executablePath":{"title":"ConfigObject.executablePath","description":"Some features, like video upload, do not work without a chrome instance. Set this to the path of your chrome instance or you can use `useChrome:true` to automatically detect a chrome instance for you. Please note, this overrides `useChrome`.","type":"string"},"useChrome":{"title":"ConfigObject.useChrome","description":"If true, the program will automatically try to detect the instance of chorme on the machine. Please note this DOES NOT override executablePath.","default":"`false`","type":"boolean"},"proxyServerCredentials":{"$ref":"#/components/schemas/ProxyServerCredentials","title":"ConfigObject.proxyServerCredentials","description":"If sent, adds a call to waPage.authenticate with those credentials. Set `corsFix` to true if using a proxy results in CORS errors."},"qrLogSkip":{"title":"ConfigObject.qrLogSkip","description":"If true, skips logging the QR Code to the console.","default":"`false`","type":"boolean"},"restartOnCrash":{"title":"ConfigObject.restartOnCrash","description":"If set, the program will try to recreate itself when the page crashes. You have to pass the function that you want called upon restart. Please note that when the page crashes you may miss some messages.\nE.g:\n```javascript\nconst start = async (client: Client) => {...}\ncreate({\n...\nrestartOnCrash: start,\n...\n})\n```"},"disableSpins":{"title":"ConfigObject.disableSpins","description":"Setting this to true will simplify logs for use within docker containers by disabling spins (will still print raw messages).","default":"`false`","type":"boolean"},"logConsole":{"title":"ConfigObject.logConsole","description":"If true, this will log any console messages from the browser.","default":"`false`","type":"boolean"},"logConsoleErrors":{"title":"ConfigObject.logConsoleErrors","description":"If true, this will log any error messages from the browser instance","default":"`false`","type":"boolean"},"authTimeout":{"title":"ConfigObject.authTimeout","description":"This determines how long the process should wait for the session authentication. If exceeded, checks if phone is out of reach (turned of or without internet connection) and throws an error. It does not relate to the amount of time spent waiting for a qr code scan (see [[qrTimeout]]). To have the system wait continuously, set this to `0`.","default":"`60`","type":"number"},"oorTimeout":{"title":"ConfigObject.oorTimeout","description":"phoneIsOutOfReach check timeout","default":"`60`","type":"number"},"killProcessOnBrowserClose":{"title":"ConfigObject.killProcessOnBrowserClose","description":"Setting this to `true` will kill the whole process when the client is disconnected from the page or if the browser is closed.","default":"`false`","type":"boolean"},"safeMode":{"title":"ConfigObject.safeMode","description":"If true, client will check if the page is valid before each command. If page is not valid, it will throw an error.","default":"`false`","type":"boolean"},"skipSessionSave":{"title":"ConfigObject.skipSessionSave","description":"If true, the process will not save a data.json file. This means that sessions will not be saved and you will need to pass sessionData as a config param or create the session data.json file yourself","default":"`false`","type":"boolean"},"popup":{"title":"ConfigObject.popup","description":"If true, the process will open a browser window where you will see basic event logs and QR codes to authenticate the session. Usually it will open on port 3000. It can also be set to a preferred port.\n\nYou can also get the QR code png at (if localhost and port 3000):\n\n`http://localhost:3000/qr`\n\nor if you have multiple session:\n\n `http://localhost:3000/qr?sessionId=[sessionId]`","default":"`false | 3000`","anyOf":[{"type":"boolean"},{"type":"number"}]},"qrPopUpOnly":{"title":"ConfigObject.qrPopUpOnly","description":"This needs to be used in conjuction with `popup`, if `popup` is not true or a number (representing a desired port) then this will not work.\n\nSetting this to true will make sure that only the qr code png is served via the web server. This is useful if you do not need the whole status page.\n\nAs mentioned in [popup](#popup), the url for the qr code is `http://localhost:3000/qr` if the port is 3000.","type":"boolean"},"inDocker":{"title":"ConfigObject.inDocker","description":"If true, the process will try infer as many config variables as possible from the environment variables. The format of the variables are as below:\n```\nsessionData ==> WA_SESSION_DATA\nsessionDataPath ==> WA_SESSION_DATA_PATH\nsessionId ==> WA_SESSION_ID\ncustomUserAgent ==> WA_CUSTOM_USER_AGENT\nblockCrashLogs ==> WA_BLOCK_CRASH_LOGS\nblockAssets ==> WA_BLOCK_ASSETS\ncorsFix ==> WA_CORS_FIX\ncacheEnabled ==> WA_CACHE_ENABLED\nheadless ==> WA_HEADLESS\nqrTimeout ==> WA_QR_TIMEOUT\nuseChrome ==> WA_USE_CHROME\nqrLogSkip ==> WA_QR_LOG_SKIP\ndisableSpins ==> WA_DISABLE_SPINS\nlogConsole ==> WA_LOG_CONSOLE\nlogConsoleErrors==> WA_LOG_CONSOLE_ERRORS\nauthTimeout ==> WA_AUTH_TIMEOUT\nsafeMode ==> WA_SAFE_MODE\nskipSessionSave ==> WA_SKIP_SESSION_SAVE\npopup ==> WA_POPUP\nlicensekey ==> WA_LICENSE_KEY\n```","default":"`false`","type":"boolean"},"qrQuality":{"$ref":"#/components/schemas/QRQuality","title":"ConfigObject.qrQuality","description":"The output quality of the qr code during authentication. This can be any increment of 0.1 from 0.1 to 1.0.","default":"`1.0`"},"qrFormat":{"$ref":"#/components/schemas/QRFormat","title":"ConfigObject.qrFormat","description":"The output format of the qr code. `png`, `jpeg` or `webm`.","default":"`png`"},"hostNotificationLang":{"$ref":"#/components/schemas/NotificationLanguage","title":"ConfigObject.hostNotificationLang","description":"The language of the host notification. See: https://github.com/open-wa/wa-automate-nodejs/issues/709#issuecomment-673419088"},"blockAssets":{"title":"ConfigObject.blockAssets","description":"Setting this to true will block all assets from loading onto the page. This may result in some load time improvements but also increases instability.","default":"`false`","type":"boolean"},"keepUpdated":{"title":"ConfigObject.keepUpdated","description":"[ALPHA FEATURE - ONLY IMPLEMENTED FOR TESTING - DO NOT USE IN PRODUCTION YET]\nSetting this to true will result in the library making sure it is always starting with the latest version of itself. This overrides `skipUpdateCheck`.","default":"`false`","type":"boolean"},"resizable":{"title":"ConfigObject.resizable","description":"Syncs the viewport size with the window size which is how normal browsers act. Only relevant when `headless: false` and this overrides `viewport` config.","default":"`true`","type":"boolean"},"viewport":{"properties":{"width":{"title":"ConfigObject.viewport.width","description":"Page width in pixels","default":"`1440`","type":"number"},"height":{"title":"ConfigObject.viewport.height","description":"Page height in pixels","default":"`900`","type":"number"}},"additionalProperties":false,"title":"ConfigObject.viewport","description":"Set the desired viewport height and width. For CLI, use [width]x[height] format. E.g `--viewport 1920x1080`.","type":"object"},"legacy":{"title":"ConfigObject.legacy","description":"As the library is constantly evolving, some parts will be replaced with more efficient and improved code. In some of the infinite edge cases these new changes may not work for you. Set this to true to roll back on 'late beta' features. The reason why legacy is false by default is that in order for features to be tested they have to be released and used by everyone to find the edge cases and fix them.","default":"`false`","type":"boolean"},"deleteSessionDataOnLogout":{"title":"ConfigObject.deleteSessionDataOnLogout","description":"Deletes the session data file (if found) on logout event. This results in a quicker login when you restart the process.","default":"`false`","type":"boolean"},"killProcessOnTimeout":{"title":"ConfigObject.killProcessOnTimeout","description":"If set to true, the system will kill the whole node process when either an [[authTimeout]] or a [[qrTimeout]] has been reached. This is useful to prevent hanging processes.","default":"`false`","type":"boolean"},"killProcessOnBan":{"title":"ConfigObject.killProcessOnBan","description":"If set to true, the system will kill the whole node process when a \"TEMPORARY BAN\" is detected. This is useful to prevent hanging processes.\nIt is `true` by default because it is a very rare event and it is better to kill the process than to leave it hanging.","default":"`true`","type":"boolean"},"corsFix":{"title":"ConfigObject.corsFix","description":"Setting this to true will bypass web security. DO NOT DO THIS IF YOU DO NOT HAVE TO. CORS issue may arise when using a proxy.","default":"`false`","type":"boolean"},"callTimeout":{"title":"ConfigObject.callTimeout","description":"Amount of time (in ms) to wait for a client method (specifically methods that interact with the WA web session) to resolve. If a client method results takes longer than the timout value then it will result in a [[PageEvaluationTimeout]] error.\n\nIf you get this error, it does not automatically mean that the method failed - it just stops your program from waiting for a client method to resolve.\n\nThis is useful if you do not rely on the results of a client method (e.g the message ID).\n\nIf set to `0`, the process will wait indefinitely for a client method to resolve.","default":"0","type":"number"},"screenshotOnInitializationBrowserError":{"title":"ConfigObject.screenshotOnInitializationBrowserError","description":"When true, this option will take a screenshot of the browser when an unexpected error occurs within the browser during `create` initialization. The path will be `[working directory]/logs/[session ID]/[start timestamp]/[timestamp].jpg`","default":"`false`","type":"boolean"},"eventMode":{"title":"ConfigObject.eventMode","description":"Setting listeners may not be your cup of tea. With eventMode, all [[SimpleListener]] events will be registered automatically and be filed via the built in Events Listener.\n\nThis is useful because you can register/deregister the event listener as needed whereas the legacy method of setting callbacks are only be set once","default":"`true`;","type":"boolean"},"logFile":{"title":"ConfigObject.logFile","description":"If true, the system will automatically create a log of all processes relating to actions sent to the web session.\n\nThe location of the file will be relative to the process directory (pd)\n\n`[pd]/[sessionId]/[start timestamp].log`","default":"false","type":"boolean"},"idCorrection":{"title":"ConfigObject.idCorrection","description":"When true, the system will attempt to correct chatIds and groupChatIds. This means you can ignore `@c.us` and `@g.us` distinctions in some parameters.","default":"false","type":"boolean"},"stickerServerEndpoint":{"title":"ConfigObject.stickerServerEndpoint","description":"Redundant until self-hostable sticker server is available.","default":"`https://sticker-api.openwa.dev`","anyOf":[{"type":"string"},{"type":"boolean"}]},"ghPatch":{"title":"ConfigObject.ghPatch","description":"This will force the library to use the default cached raw github link for patches to shave a few hundred milliseconds from your launch time. If you use this option, you will need to wait about 5 minutes before trying out new patches.","default":"`false`","type":"boolean"},"cachedPatch":{"title":"ConfigObject.cachedPatch","description":"Setting this to `true` will save a local copy of the patches.json file (as patches.ignore.data.json) which will be used in subsequent instantiations of the session. While the rest of the launch procedure is running, the library will fetch and save a recent version of the patches to ensure your patches don't go stale. This will be ignored if the cached patches are more than a day old.","default":"`false`","type":"boolean"},"logDebugInfoAsObject":{"title":"ConfigObject.logDebugInfoAsObject","description":"Setting `this` to true will replace the `console.table` with a stringified logging of the debug info object instead. This would be useful to set for smaller terminal windows. If `disableSpins` is `true` then this will also be `true`.","default":"`false`","type":"boolean"},"logInternalEvents":{"title":"ConfigObject.logInternalEvents","description":"This will make the library log all internal wa web events to the console. This is useful for debugging purposes. DO NOT TURN THIS ON UNLESS ASKED TO.","type":"boolean"},"killClientOnLogout":{"title":"ConfigObject.killClientOnLogout","description":"Kill the client when a logout is detected","default":"`false`","type":"boolean"},"throwOnExpiredSessionData":{"title":"ConfigObject.throwOnExpiredSessionData","description":"This will make the `create` command return `false` if the detected session data is expired.\n\nThis will mean, the process will not attempt to automatically get a new QR code.","default":"`false`","type":"boolean"},"useNativeProxy":{"title":"ConfigObject.useNativeProxy","description":"Some sessions may experience issues with sending media when using proxies. Using the native proxy system instead of the recommended 3rd party library may fix these issues.","default":"`false`","type":"boolean"},"raspi":{"title":"ConfigObject.raspi","description":"Set this to `true` to make the library work on Raspberry Pi OS.\n\nMake sure to run the following command before running the library the first time:\n\n```\n> sudo apt update -y && sudo apt install chromium-browser chromium-codecs-ffmpeg -y && sudo apt upgrade\n```\n\nIf you're using the CLI, you can set this value to `true` by adding the following flag to the CLI command\n\n```\n> npx @open-wa/wa-automate ... --raspi\n```","default":"`false`","type":"boolean"},"pQueueDefault":{"title":"ConfigObject.pQueueDefault","description":"Default pqueue options applied to all listeners that can take pqueue options as a second optional parameter. For now, this only includes `onMessage` and `onAnyMessage`.\n\nSee: https://github.com/sindresorhus/p-queue#options\n\nExample: process 5 events within every 3 seconds window. Make sure to only process at most 2 at any one time. Make sure there is at least 100ms between each event processing.\n\n```javascript\n {\n intervalCap: 5, //process 5 events\n interval: 3000, //within every three second window\n concurrency: 2, //make sure to process, at most, 2 events at any one time\n timeout: 100, //make sure there is a 100ms gap between each event processing.\n carryoverConcurrencyCount: true //If there are more than 5 events in that period, process them within the next 3 second period. Make sure this is always set to true!!!\n }\n```","default":"`undefined`"},"messagePreprocessor":{"title":"ConfigObject.messagePreprocessor","description":"Set a preprocessor, or multiple chained preprocessors, for messages. See [MPConfigType](/) for more info.\n\noptions: `SCRUB`, `BODY_ONLY`, `AUTO_DECRYPT`, `AUTO_DECRYPT_SAVE`, `UPLOAD_CLOUD`.","default":"`undefined`"},"preprocFilter":{"title":"ConfigObject.preprocFilter","description":"Set an array filter to be used with messagePreprocessor to limit which messages are preprocessed.\n\nE.g if you want to scrub all messages that are not from a group, you can do the following:\n`\"m=>!m.isGroupMsg\"`","default":"`undefined`","type":"string"},"cloudUploadOptions":{"properties":{"provider":{"$ref":"#/components/schemas/CLOUD_PROVIDERS","title":"ConfigObject.cloudUploadOptions.provider","description":"`AWS`, `GCP` or `WASABI`\n\nenv: `OW_CLOUD_ACCESS_KEY_ID`"},"accessKeyId":{"title":"ConfigObject.cloudUploadOptions.accessKeyId","description":"S3 compatible access key ID.\n\ne.g: `AKIAIOSFODNN7EXAMPLE` or `GOOGTS7C7FUP3AIRVJTE2BCD`\n\nenv: `OW_CLOUD_ACCESS_KEY_ID`","type":"string"},"secretAccessKey":{"title":"ConfigObject.cloudUploadOptions.secretAccessKey","description":"S3 compatible secret access key.\n\ne.g `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`\n\nenv: `OW_CLOUD_SECRET_ACCESS_KEY`","type":"string"},"bucket":{"title":"ConfigObject.cloudUploadOptions.bucket","description":"Bucket name\n\nenv: `OW_CLOUD_BUCKET`","type":"string"},"region":{"title":"ConfigObject.cloudUploadOptions.region","description":"Bucket region.\n\nNot required for `GCP` provider\n\nenv: `OW_CLOUD_REGION`","type":"string"},"ignoreHostAccount":{"title":"ConfigObject.cloudUploadOptions.ignoreHostAccount","description":"Ignore processing of messages that are sent by the host account itself\n\nenv: `OW_CLOUD_IGNORE_HOST`","type":"boolean"},"directory":{"anyOf":[{"$ref":"#/components/schemas/DIRECTORY_STRATEGY","title":"ConfigObject.cloudUploadOptions.directory"},{"title":"ConfigObject.cloudUploadOptions.directory","type":"string"}],"title":"ConfigObject.cloudUploadOptions.directory","description":"The directory strategy to use when uploading files. Or just set it to a custom directory string.\n\nenv: `OW_DIRECTORY`"},"public":{"title":"ConfigObject.cloudUploadOptions.public","description":"Setting this to true will make the uploaded file public","type":"boolean"},"headers":{"additionalProperties":{"type":"string"},"title":"ConfigObject.cloudUploadOptions.headers","description":"Extra headers to add to the upload request","type":"object"}},"required":["provider","accessKeyId","secretAccessKey","bucket"],"additionalProperties":false,"title":"ConfigObject.cloudUploadOptions","description":"REQUIRED IF `messagePreprocessor` IS SET TO `UPLOAD_CLOUD`.\n\nThis can be set via the config or the corresponding environment variables.","type":"object"},"onError":{"$ref":"#/components/schemas/OnError","title":"ConfigObject.onError","description":"What to do when an error is detected on a client method.","default":"`OnError.NOTHING`"},"multiDevice":{"title":"ConfigObject.multiDevice","description":"Please note that multi-device is still in beta so a lot of things may not work. It is HIGHLY suggested to NOT use this in production!!!!\n\nSet this to true if you're using the multidevice beta.","default":"`true`\n:::danger\nSome features (e.g [[sendLinkWithAutoPreview]]) **do not** work with multi-device beta. Check [this `api`](#).\n:::","type":"boolean"},"sessionDataBucketAuth":{"title":"ConfigObject.sessionDataBucketAuth","description":"Base64 encoded S3 Bucket & Authentication object for session data files. The object should be in the same format as cloudUploadOptions.","type":"string"},"autoEmoji":{"anyOf":[{"title":"ConfigObject.autoEmoji","type":"string"},{"title":"ConfigObject.autoEmoji","enum":[false],"type":"boolean"}],"title":"ConfigObject.autoEmoji","description":"Set the automatic emoji detection character. Set this to `false` to disable auto emoji. Default is `:`.","default":"`:`"},"maxChats":{"title":"ConfigObject.maxChats","description":"Set the maximum amount of chats to be present in a session.","type":"number"},"maxMessages":{"title":"ConfigObject.maxMessages","description":"Set the maximum amount of messages to be present in a session.","type":"number"},"discord":{"title":"ConfigObject.discord","description":"Your Discord ID to get onto the sticker leaderboard!","type":"string"},"ignoreNuke":{"title":"ConfigObject.ignoreNuke","description":"Don't implicitly determine if the host logged out.","type":"boolean"},"ensureHeadfulIntegrity":{"title":"ConfigObject.ensureHeadfulIntegrity","description":"Makes sure the headless session is usable even on first login.\nHeadful sessions are ususally only usable on reauthentication.","type":"boolean"},"waitForRipeSession":{"title":"ConfigObject.waitForRipeSession","description":"wait for a valid headful session. Not required in recent versions.\ndefault: `true`","type":"boolean"},"qrMax":{"title":"ConfigObject.qrMax","description":"Automatically kill the process after a set amount of qr codes","type":"number"},"ezqr":{"title":"ConfigObject.ezqr","description":"Expose a URL where you can easily scan the qr code","type":"boolean"},"logging":{"items":{"$ref":"#/components/schemas/ConfigLogTransport","title":"ConfigObject.logging.[]"},"title":"ConfigObject.logging","description":"An array of [winston](https://github.com/winstonjs/winston/blob/master/docs/transports.md#additional-transports) logging transport configurations.\n\n[Check this discussion to see how to set up logging](https://github.com/open-wa/wa-automate-nodejs/discussions/2373)","type":"array"},"linkParser":{"title":"ConfigObject.linkParser","description":"The URL of your instance of [serverless meta grabber](https://github.com/RemiixInc/meta-grabber-serverless) by [RemiixInc](https://github.com/RemiixInc).\n\ndefault: `https://link.openwa.cloud/api`","type":"string"},"aggressiveGarbageCollection":{"title":"ConfigObject.aggressiveGarbageCollection","description":"Setting this to true will run `gc()` on before every command sent to the browser.\n\nThis is experimental and may not work or it may have unforeseen sideeffects.","type":"boolean"}},"additionalProperties":{},"title":"ConfigObject","type":"object"},"AdvancedConfig":{"allOf":[{"$ref":"#/components/schemas/ConfigObject"},{"properties":{"licenseKey":{"$ref":"#/components/schemas/LicenseKeyConfig","title":"licenseKey"}},"required":["licenseKey"],"additionalProperties":false,"type":"object"}],"title":"AdvancedConfig"},"LicenseKey":{"title":"LicenseKey","type":"string"},"LicenseKeyConfig":{"anyOf":[{"$ref":"#/components/schemas/LicenseKeyConfigFunction","title":"LicenseKeyConfig"},{"$ref":"#/components/schemas/LicenseKeyConfigObject","title":"LicenseKeyConfig"},{"$ref":"#/components/schemas/LicenseKey","title":"LicenseKeyConfig"}],"title":"LicenseKeyConfig"},"NumberCheck":{"properties":{"id":{"$ref":"#/components/schemas/Id","title":"NumberCheck.id"},"status":{"enum":[200,404],"title":"NumberCheck.status","type":"number"},"isBusiness":{"title":"NumberCheck.isBusiness","type":"boolean"},"canReceiveMessage":{"title":"NumberCheck.canReceiveMessage","type":"boolean"},"numberExists":{"title":"NumberCheck.numberExists","type":"boolean"}},"required":["id","status","isBusiness","canReceiveMessage","numberExists"],"additionalProperties":false,"title":"NumberCheck","type":"object"},"BizCategory":{"properties":{"id":{"title":"BizCategory.id","type":"string"},"localized_display_name":{"title":"BizCategory.localized_display_name","type":"string"}},"required":["id","localized_display_name"],"additionalProperties":false,"title":"BizCategory","type":"object"},"BizProfileOptions":{"properties":{"commerceExperience":{"enum":["catalog","none","shop"],"title":"BizProfileOptions.commerceExperience","type":"string"},"cartEnabled":{"title":"BizProfileOptions.cartEnabled","type":"boolean"}},"required":["commerceExperience","cartEnabled"],"additionalProperties":false,"title":"BizProfileOptions","type":"object"},"BusinessHours":{"properties":{"config":{"title":"BusinessHours.config"},"timezone":{"title":"BusinessHours.timezone","type":"string"}},"required":["config","timezone"],"additionalProperties":false,"title":"BusinessHours","type":"object"},"BusinessProfile":{"properties":{"id":{"$ref":"#/components/schemas/ContactId","title":"BusinessProfile.id","description":"The Contact ID of the business"},"tag":{"title":"BusinessProfile.tag","description":"Some special string that identifies the business (?)","type":"string"},"description":{"title":"BusinessProfile.description","description":"The business description","type":"string"},"categories":{"items":{"$ref":"#/components/schemas/BizCategory","title":"BusinessProfile.categories.[]"},"title":"BusinessProfile.categories","description":"The business' categories","type":"array"},"profileOptions":{"$ref":"#/components/schemas/BizProfileOptions","title":"BusinessProfile.profileOptions","description":"The business' profile options"},"email":{"title":"BusinessProfile.email","description":"The business' email address","type":"string"},"website":{"items":{"title":"BusinessProfile.website.[]","type":"string"},"title":"BusinessProfile.website","description":"Array of strings that represent the business' websites","type":"array"},"businessHours":{"$ref":"#/components/schemas/BusinessHours","title":"BusinessProfile.businessHours","description":"The operating hours of the business"},"catalogStatus":{"title":"BusinessProfile.catalogStatus","description":"The status of the business' catalog","type":"string"},"address":{"title":"BusinessProfile.address","description":"The address of the business","type":"string"},"fbPage":{"title":"BusinessProfile.fbPage","description":"The facebook page of the business"},"igProfessional":{"title":"BusinessProfile.igProfessional","description":"The instagram profile of the business"},"isProfileLinked":{"title":"BusinessProfile.isProfileLinked","type":"boolean"},"coverPhoto":{"properties":{"id":{"title":"BusinessProfile.coverPhoto.id","description":"The id of the cover photo","type":"string"},"url":{"title":"BusinessProfile.coverPhoto.url","description":"The URL of the cover photo. It might download as an .enc but just change the extension to .jpg","type":"string"}},"required":["id","url"],"additionalProperties":false,"title":"BusinessProfile.coverPhoto","type":"object"},"latitude":{"title":"BusinessProfile.latitude","description":"The latitude of the business location if set","type":"number"},"longitude":{"title":"BusinessProfile.longitude","description":"The longitude of the business location if set","type":"number"}},"required":["id","tag","description","categories","profileOptions","email","website","businessHours","catalogStatus","address","fbPage","igProfessional","isProfileLinked","coverPhoto","latitude","longitude"],"additionalProperties":false,"title":"BusinessProfile","type":"object"},"Contact":{"properties":{"formattedName":{"title":"Contact.formattedName","type":"string"},"id":{"$ref":"#/components/schemas/ContactId","title":"Contact.id"},"isBusiness":{"title":"Contact.isBusiness","type":"boolean"},"isEnterprise":{"title":"Contact.isEnterprise","description":"Most likely true when the account has a green tick. See `verifiedLevel` also.","type":"boolean"},"isMe":{"title":"Contact.isMe","type":"boolean"},"isMyContact":{"title":"Contact.isMyContact","type":"boolean"},"isPSA":{"title":"Contact.isPSA","type":"boolean"},"isUser":{"title":"Contact.isUser","type":"boolean"},"isWAContact":{"title":"Contact.isWAContact","type":"boolean"},"labels":{"items":{"title":"Contact.labels.[]","type":"string"},"title":"Contact.labels","type":"array"},"msgs":{"items":{"$ref":"#/components/schemas/Message","title":"Contact.msgs.[]"},"title":"Contact.msgs","type":"array"},"name":{"title":"Contact.name","type":"string"},"plaintextDisabled":{"title":"Contact.plaintextDisabled","type":"boolean"},"profilePicThumbObj":{"properties":{"eurl":{"title":"Contact.profilePicThumbObj.eurl","type":"string"},"id":{"$ref":"#/components/schemas/Id","title":"Contact.profilePicThumbObj.id"},"img":{"title":"Contact.profilePicThumbObj.img","type":"string"},"imgFull":{"title":"Contact.profilePicThumbObj.imgFull","type":"string"},"raw":{"title":"Contact.profilePicThumbObj.raw","type":"string"},"tag":{"title":"Contact.profilePicThumbObj.tag","type":"string"}},"required":["eurl","id","img","imgFull","raw","tag"],"additionalProperties":false,"title":"Contact.profilePicThumbObj","type":"object"},"pushname":{"title":"Contact.pushname","type":"string"},"shortName":{"title":"Contact.shortName","type":"string"},"statusMute":{"title":"Contact.statusMute","type":"boolean"},"type":{"title":"Contact.type","type":"string"},"verifiedLevel":{"title":"Contact.verifiedLevel","description":"0 = not verified\n2 = verified (most likely represents a blue tick)","type":"number"},"verifiedName":{"title":"Contact.verifiedName","description":"The business account name verified by WA.","type":"string"},"isOnline":{"title":"Contact.isOnline","type":"boolean"},"lastSeen":{"title":"Contact.lastSeen","type":"number"},"businessProfile":{"$ref":"#/components/schemas/BusinessProfile","title":"Contact.businessProfile","description":"If the contact is a business, the business information will be added to the contact object.\n\nIn some circumstances this will be out of date or lacking certain fields. In those cases you have to use `client.getBusinessProfile`"}},"required":["formattedName","id","isBusiness","isEnterprise","isMe","isMyContact","isPSA","isUser","isWAContact","labels","msgs","name","plaintextDisabled","profilePicThumbObj","pushname","shortName","statusMute","type","verifiedLevel","verifiedName"],"additionalProperties":false,"title":"Contact","type":"object"},"Participant":{"properties":{"contact":{"$ref":"#/components/schemas/Contact","title":"Participant.contact"},"id":{"$ref":"#/components/schemas/NonSerializedId","title":"Participant.id"},"isAdmin":{"title":"Participant.isAdmin","type":"boolean"},"isSuperAdmin":{"title":"Participant.isSuperAdmin","type":"boolean"}},"required":["contact","id","isAdmin","isSuperAdmin"],"additionalProperties":false,"title":"Participant","type":"object"},"GroupMetadata":{"properties":{"id":{"$ref":"#/components/schemas/GroupChatId","title":"GroupMetadata.id","description":"The chat id of the group [[GroupChatId]]"},"creation":{"title":"GroupMetadata.creation","description":"The timestamp of when the group was created","type":"number"},"owner":{"$ref":"#/components/schemas/NonSerializedId","title":"GroupMetadata.owner","description":"The id of the owner of the group [[ContactId]]"},"participants":{"items":{"$ref":"#/components/schemas/Participant","title":"GroupMetadata.participants.[]"},"title":"GroupMetadata.participants","description":"An array of participants in the group","type":"array"},"pendingParticipants":{"items":{"$ref":"#/components/schemas/Participant","title":"GroupMetadata.pendingParticipants.[]"},"title":"GroupMetadata.pendingParticipants","description":"Unknown.","type":"array"},"desc":{"title":"GroupMetadata.desc","description":"The description of the group","type":"string"},"descOwner":{"$ref":"#/components/schemas/ContactId","title":"GroupMetadata.descOwner","description":"The account that set the description last."},"trusted":{"title":"GroupMetadata.trusted","type":"boolean"},"suspended":{"title":"GroupMetadata.suspended","description":"Not sure what this represents","type":"boolean"},"support":{"title":"GroupMetadata.support","description":"Not sure what this represents","type":"boolean"},"isParentGroup":{"title":"GroupMetadata.isParentGroup","description":"Is this group a parent group (a.k.a community)","type":"boolean"},"groupType":{"enum":["DEAFULT","SUBGROUP","COMMUNITY"],"title":"GroupMetadata.groupType","description":"The type of group","type":"string"},"defaultSubgroup":{"title":"GroupMetadata.defaultSubgroup","description":"Communities have a default group chat","type":"boolean"},"isParentGroupClosed":{"title":"GroupMetadata.isParentGroupClosed","type":"boolean"},"joinedSubgroups":{"items":{"$ref":"#/components/schemas/GroupId","title":"GroupMetadata.joinedSubgroups.[]"},"title":"GroupMetadata.joinedSubgroups","description":"List of Group IDs that the host account has joined as part of this community","type":"array"}},"required":["id","creation","owner","participants","pendingParticipants","groupType","defaultSubgroup","isParentGroupClosed","joinedSubgroups"],"additionalProperties":false,"title":"GroupMetadata","type":"object"},"ParticipantChangedEventModel":{"properties":{"by":{"$ref":"#/components/schemas/ContactId","title":"ParticipantChangedEventModel.by"},"action":{"$ref":"#/components/schemas/groupChangeEvent","title":"ParticipantChangedEventModel.action"},"who":{"items":{"$ref":"#/components/schemas/ContactId","title":"ParticipantChangedEventModel.who.[]"},"title":"ParticipantChangedEventModel.who","type":"array"},"chat":{"$ref":"#/components/schemas/ChatId","title":"ParticipantChangedEventModel.chat"}},"required":["by","action","who","chat"],"additionalProperties":false,"title":"ParticipantChangedEventModel","type":"object"},"NewCommunityGroup":{"properties":{"subject":{"title":"NewCommunityGroup.subject","type":"string"},"icon":{"$ref":"#/components/schemas/DataURL","title":"NewCommunityGroup.icon"},"ephemeralDuration":{"title":"NewCommunityGroup.ephemeralDuration","type":"number"}},"required":["subject"],"additionalProperties":false,"title":"NewCommunityGroup","description":"Used when creating a new community with.","type":"object"},"GenericGroupChangeEvent":{"properties":{"author":{"$ref":"#/components/schemas/Contact","title":"GenericGroupChangeEvent.author","description":"The contact who triggered this event. (E.g the contact who changed the group picture)"},"body":{"title":"GenericGroupChangeEvent.body","description":"Some more information about the event","type":"string"},"groupMetadata":{"$ref":"#/components/schemas/GroupMetadata","title":"GenericGroupChangeEvent.groupMetadata"},"groupPic":{"title":"GenericGroupChangeEvent.groupPic","description":"Base 64 encoded image","type":"string"},"id":{"$ref":"#/components/schemas/MessageId","title":"GenericGroupChangeEvent.id"},"type":{"enum":["picutre","create","delete","subject","revoke_invite","description","restrict","announce","no_frequently_forwarded","announce_msg_bounce","add","remove","demote","promote","invite","leave","modify","v4_add_invite_sent","v4_add_invite_join","growth_locked","growth_unlocked","linked_group_join"],"title":"GenericGroupChangeEvent.type","description":"Type of the event","type":"string"}},"required":["author","body","groupMetadata","groupPic","id","type"],"additionalProperties":false,"title":"GenericGroupChangeEvent","type":"object"},"Id":{"properties":{"server":{"title":"Id.server","type":"string"},"user":{"title":"Id.user","type":"string"},"_serialized":{"title":"Id._serialized","type":"string"}},"required":["server","user","_serialized"],"additionalProperties":false,"title":"Id","type":"object"},"EasyApiResponse":{"properties":{"success":{"title":"EasyApiResponse.success","type":"boolean"},"response":{"title":"EasyApiResponse.response"}},"required":["success","response"],"additionalProperties":false,"title":"EasyApiResponse","type":"object"},"Label":{"properties":{"id":{"title":"Label.id","description":"The internal ID of the label. Usually a number represented as a string e.g \"1\"","type":"string"},"name":{"title":"Label.name","description":"The text contents of the label","type":"string"},"items":{"items":{"properties":{"type":{"enum":["Chat","Contact","Message"],"title":"Label.items.[].type","description":"Labels can be applied to chats, contacts or individual messages. This represents the type of object the label is attached to.","type":"string"},"id":{"anyOf":[{"$ref":"#/components/schemas/ContactId","title":"Label.items.[].id"},{"$ref":"#/components/schemas/ChatId","title":"Label.items.[].id"},{"$ref":"#/components/schemas/MessageId","title":"Label.items.[].id"}],"title":"Label.items.[].id","description":"The ID of the object that the label is atteched to."}},"required":["type","id"],"additionalProperties":false,"title":"Label.items.[]","type":"object"},"title":"Label.items","description":"The items that are tagged with this label","type":"array"}},"required":["id","name","items"],"additionalProperties":false,"title":"Label","type":"object"},"StickerMetadata":{"properties":{"author":{"title":"StickerMetadata.author","description":"The author of the sticker","default":"``","type":"string"},"pack":{"title":"StickerMetadata.pack","description":"The pack of the sticker","default":"``","type":"string"},"removebg":{"anyOf":[{"title":"StickerMetadata.removebg","type":"boolean"},{"title":"StickerMetadata.removebg","enum":["HQ"],"type":"string"}],"title":"StickerMetadata.removebg","description":"ALPHA FEATURE - NO GUARANTEES IT WILL WORK AS EXPECTED:\n\n[REQUIRES AN INSIDERS LICENSE-KEY](https://gum.co/open-wa?tier=Insiders%20Program)\n\nAttempt to remove the background of the sticker. Only valid for paid licenses.\n\noptions:\n\n `true` - remove background after resizing\n\n `HQ` - remove background before resizing (i.e on original photo)","default":"`false`"},"keepScale":{"title":"StickerMetadata.keepScale","description":"Setting this to `true` will skip the resizing/square-cropping of the sticker. It will instead 'letterbox' the image with a transparent background.","type":"boolean"},"circle":{"title":"StickerMetadata.circle","description":"Applies a circular mask to the sticker. Works on images only for now.","type":"boolean"},"discord":{"title":"StickerMetadata.discord","description":"Your Discord ID to get onto the sticker leaderboard!","type":"string"},"cropPosition":{"enum":["top","right top","right","right bottom","bottom","left bottom","left","left top","north","northeast","east","southeast","south","southwest","west","northwest","center","centre","entropy","attention"],"title":"StickerMetadata.cropPosition","description":"Crop position\n\nLearn more: https://sharp.pixelplumbing.com/api-resize","default":"`attention`","type":"string"},"cornerRadius":{"title":"StickerMetadata.cornerRadius","description":"The corner radius of the sticker when `stickerMetadata.circle` is set to true.\n@minimum `1`\n@maximum `100`\n@multipleOf `1`","default":"`100`","type":"number"}},"required":["author","pack"],"additionalProperties":false,"title":"StickerMetadata","type":"object"},"Mp4StickerConversionProcessOptions":{"properties":{"fps":{"title":"Mp4StickerConversionProcessOptions.fps","description":"Desired Frames per second of the sticker output","default":"`10`","type":"number"},"startTime":{"title":"Mp4StickerConversionProcessOptions.startTime","description":"The video start time of the sticker","default":"`00:00:00.0`","type":"string"},"endTime":{"title":"Mp4StickerConversionProcessOptions.endTime","description":"The video end time of the sticker. By default, stickers are made from the first 5 seconds of the video","default":"`00:00:05.0`","type":"string"},"loop":{"title":"Mp4StickerConversionProcessOptions.loop","description":"The amount of times the video loops in the sticker. To save processing time, leave this as 0\ndefault `0`","type":"number"},"crop":{"title":"Mp4StickerConversionProcessOptions.crop","description":"Centres and crops the video.\ndefault `true`","type":"boolean"},"log":{"title":"Mp4StickerConversionProcessOptions.log","description":"Prints ffmpeg logs in the terminal","default":"`false`","type":"boolean"},"square":{"title":"Mp4StickerConversionProcessOptions.square","description":"A number representing the WxH of the output sticker (default `512x512`). Lowering this number is a great way to process longer duration stickers. The max value is `512`.\ndefault `512`","type":"number"}},"additionalProperties":false,"title":"Mp4StickerConversionProcessOptions","type":"object"},"MessagePinDuration":{"enum":["FifteenSeconds","FiveSeconds","OneDay","OneMinute","SevenDays","ThirtyDays"],"title":"MessagePinDuration","type":"string"},"Message":{"properties":{"selectedButtonId":{"title":"Message.selectedButtonId","description":"The ID of the selected button","type":"string"},"id":{"$ref":"#/components/schemas/MessageId","title":"Message.id","description":"The id of the message. Consists of the Chat ID and a unique string.\n\nExample:\n\n```\nfalse_447123456789@c.us_7D914FEA78BE10277743F4B785045C37\n```"},"mId":{"title":"Message.mId","description":"The unique segment of the message id.\n\nExample:\n\n```\n7D914FEA78BE10277743F4B785045C37\n```","type":"string"},"body":{"title":"Message.body","description":"The body of the message. If the message type is `chat` , `body` will be the text of the chat. If the message type is some sort of media, then this body will be the thumbnail of the media.","type":"string"},"device":{"title":"Message.device","description":"The device ID of the device that sent the message. This is only present if the message was sent from host account-linked session. This is useful for determining if a message was sent from a different mobile device (note that whenever a device) or a desktop session.\n\nNote: This will emit a number for the current controlled session also but the only way to know if the number represents the current session is by checking `local` (it will be `true` if the message was sent from the current session).\n\nIf the device ID is `0` then the message was sent from the \"root\" host account device.\n\nThis might be undefined for incoming messages.","type":"number"},"local":{"title":"Message.local","description":"If the message was sent from this controlled session this will be `true`. This is useful for determining if a message was sent from a different mobile device (note that whenever a device) or a desktop session.","type":"boolean"},"text":{"title":"Message.text","description":"a convenient way to get the main text content from a message.","type":"string"},"type":{"$ref":"#/components/schemas/MessageTypes","title":"Message.type","description":"The type of the message, see [[MessageTypes]]"},"filehash":{"title":"Message.filehash","description":"Used to checking the integrity of the decrypted media.","type":"string"},"mimetype":{"title":"Message.mimetype","type":"string"},"lat":{"title":"Message.lat","description":"The latitude of a location message","type":"string"},"lng":{"title":"Message.lng","description":"The longitude of a location message","type":"string"},"loc":{"title":"Message.loc","description":"The text associated with a location message","type":"string"},"t":{"title":"Message.t","description":"The timestamp of the message","type":"number"},"notifyName":{"title":"Message.notifyName","type":"string"},"from":{"$ref":"#/components/schemas/ChatId","title":"Message.from","description":"The chat from which the message was sent"},"to":{"$ref":"#/components/schemas/ChatId","title":"Message.to","description":"The chat id to which the message is being sent"},"self":{"enum":["in","out"],"title":"Message.self","description":"Indicates whether the message is coming into the session or going out of the session. You can have a message sent by the host account show as `in` when the message was sent from another\nsession or from the host account device itself.","type":"string"},"duration":{"title":"Message.duration","description":"The length of the media in the message, if it exists.","anyOf":[{"type":"string"},{"type":"number"}]},"ack":{"$ref":"#/components/schemas/MessageAck","title":"Message.ack","description":"The acknolwedgement state of a message [[MessageAck]]"},"invis":{"title":"Message.invis","type":"boolean"},"isNewMsg":{"title":"Message.isNewMsg","type":"boolean"},"star":{"title":"Message.star","type":"boolean"},"recvFresh":{"title":"Message.recvFresh","type":"boolean"},"broadcast":{"title":"Message.broadcast","description":"If the message is sent as a broadcast","type":"boolean"},"isForwarded":{"title":"Message.isForwarded","description":"If the message has been forwarded","type":"boolean"},"labels":{"items":{"title":"Message.labels.[]","type":"string"},"title":"Message.labels","description":"The labels associated with the message (used with business accounts)","type":"array"},"mentionedJidList":{"items":{"$ref":"#/components/schemas/ContactId","title":"Message.mentionedJidList.[]"},"title":"Message.mentionedJidList","description":"An array of all mentioned numbers in this message.","type":"array"},"caption":{"title":"Message.caption","description":"If the message is of a media type, it may also have a caption","type":"string"},"sender":{"$ref":"#/components/schemas/Contact","title":"Message.sender","description":"The contact object of the account that sent the message"},"timestamp":{"title":"Message.timestamp","description":"the timestanmp of the message","type":"number"},"filePath":{"title":"Message.filePath","description":"When `config.messagePreprocessor: \"AUTO_DECRYPT_SAVE\"` is set, media is decrypted and saved on disk in a folder called media relative to the current working directory.\n\nThis is the filePath of the decrypted file.","type":"string"},"filename":{"title":"Message.filename","description":"The given filename of the file","type":"string"},"content":{"title":"Message.content","type":"string"},"isGroupMsg":{"title":"Message.isGroupMsg","type":"boolean"},"isMMS":{"title":"Message.isMMS","type":"boolean"},"isMedia":{"title":"Message.isMedia","type":"boolean"},"isNotification":{"title":"Message.isNotification","type":"boolean"},"isPSA":{"title":"Message.isPSA","type":"boolean"},"fromMe":{"title":"Message.fromMe","description":"If the message is from the host account","type":"boolean"},"chat":{"$ref":"#/components/schemas/Chat","title":"Message.chat","description":"The chat object"},"chatId":{"$ref":"#/components/schemas/ChatId","title":"Message.chatId"},"author":{"title":"Message.author","type":"string"},"stickerAuthor":{"title":"Message.stickerAuthor","type":"string"},"stickerPack":{"title":"Message.stickerPack","type":"string"},"clientUrl":{"title":"Message.clientUrl","description":"@deprecated Ironically, you should be using `deprecatedMms3Url` instead","type":"string"},"deprecatedMms3Url":{"title":"Message.deprecatedMms3Url","type":"string"},"isQuotedMsgAvailable":{"title":"Message.isQuotedMsgAvailable","description":"If this message is quoting (replying to) another message","type":"boolean"},"quotedMsg":{"$ref":"#/components/schemas/Message","title":"Message.quotedMsg"},"quotedMsgObj":{"$ref":"#/components/schemas/Message","title":"Message.quotedMsgObj"},"isGroupJoinRequest":{"$ref":"#/components/schemas/GroupChatId","title":"Message.isGroupJoinRequest","description":"When a user requests to join a group wihtin a community the request is received by the host as a message. This boolean will allow you to easily determine if the incoming message is a request to join a group.\n\nIf this is `true` then you need to determine within your own code whether or not to accept the user to the group which is indicated with `quotedRemoteJid` using `addParticipant`."},"senderId":{"title":"Message.senderId","description":"The ID of the message sender","type":"string"},"quotedRemoteJid":{"title":"Message.quotedRemoteJid","description":"The ID of the quoted group. Usually present when a user is requesting to join a group.","type":"string"},"quotedParentGroupJid":{"$ref":"#/components/schemas/GroupChatId","title":"Message.quotedParentGroupJid","description":"The parent group ID (community ID - communities are just groups made up of other groups) of the group represented by `quotedRemoteJid`"},"mediaData":{"title":"Message.mediaData"},"shareDuration":{"title":"Message.shareDuration","type":"number"},"isAnimated":{"title":"Message.isAnimated","type":"boolean"},"ctwaContext":{"properties":{"sourceUrl":{"title":"Message.ctwaContext.sourceUrl","type":"string"},"thumbnail":{"title":"Message.ctwaContext.thumbnail","nullable":true,"type":"string"},"mediaType":{"title":"Message.ctwaContext.mediaType","type":"number"},"isSuspiciousLink":{"title":"Message.ctwaContext.isSuspiciousLink","nullable":true,"type":"boolean"}},"required":["sourceUrl","thumbnail","mediaType","isSuspiciousLink"],"additionalProperties":false,"title":"Message.ctwaContext","type":"object"},"isViewOnce":{"title":"Message.isViewOnce","description":"Is the message a \"view once\" message","type":"boolean"},"quoteMap":{"$ref":"#/components/schemas/QuoteMap","title":"Message.quoteMap","description":"Use this to traverse the quote chain."},"cloudUrl":{"title":"Message.cloudUrl","description":"The URL of the file after being uploaded to the cloud using a cloud upload message preprocessor.","type":"string"},"buttons":{"items":{"$ref":"#/components/schemas/Button","title":"Message.buttons.[]"},"title":"Message.buttons","description":"Buttons associated with the message","type":"array"},"listResponse":{"$ref":"#/components/schemas/Row","title":"Message.listResponse","description":"List response associated with the message"},"list":{"properties":{"\"sections\"":{"items":{"$ref":"#/components/schemas/Section","title":"Message.list.\"sections\".[]"},"title":"Message.list.\"sections\"","type":"array"},"\"title\"":{"title":"Message.list.\"title\"","type":"string"},"\"description\"":{"title":"Message.list.\"description\"","type":"string"},"\"buttonText\"":{"title":"Message.list.\"buttonText\"","type":"string"}},"required":["\"sections\"","\"title\"","\"description\"","\"buttonText\""],"additionalProperties":false,"title":"Message.list","description":"The list associated with the list message","type":"object"},"pollOptions":{"items":{"$ref":"#/components/schemas/PollOption","title":"Message.pollOptions.[]"},"title":"Message.pollOptions","description":"The options of a poll","type":"array"},"reactionByMe":{"$ref":"#/components/schemas/ReactionSender","title":"Message.reactionByMe","description":"The reaction of the host account to this message"},"reactions":{"items":{"properties":{"aggregateEmoji":{"title":"Message.reactions.[].aggregateEmoji","type":"string"},"senders":{"items":{"$ref":"#/components/schemas/ReactionSender","title":"Message.reactions.[].senders.[]"},"title":"Message.reactions.[].senders","description":"The senders of this reaction","type":"array"},"hasReactionByMe":{"title":"Message.reactions.[].hasReactionByMe","description":"If the host account has reacted to this message with this reaction","type":"boolean"},"id":{"title":"Message.reactions.[].id","description":"The message ID of the reaction itself","type":"string"}},"required":["aggregateEmoji","senders","hasReactionByMe","id"],"additionalProperties":false,"title":"Message.reactions.[]","type":"object"},"title":"Message.reactions","type":"array"}},"required":["selectedButtonId","id","mId","body","device","local","text","type","t","notifyName","from","to","self","ack","invis","isNewMsg","star","recvFresh","broadcast","isForwarded","labels","mentionedJidList","caption","sender","timestamp","content","isGroupMsg","isMMS","isMedia","isNotification","isPSA","fromMe","chat","chatId","author","clientUrl","deprecatedMms3Url","isQuotedMsgAvailable","mediaData","shareDuration","isAnimated","isViewOnce","quoteMap","reactions"],"additionalProperties":false,"title":"Message","type":"object"},"ReactionSender":{"properties":{"parentMsgKey":{"$ref":"#/components/schemas/MessageId","title":"ReactionSender.parentMsgKey","description":"The ID of the message being reacted to"},"senderUserJid":{"$ref":"#/components/schemas/ContactId","title":"ReactionSender.senderUserJid","description":"The contact ID of the sender of the reaction"},"msgKey":{"$ref":"#/components/schemas/MessageId","title":"ReactionSender.msgKey","description":"The message ID of the reaction itself"},"reactionText":{"title":"ReactionSender.reactionText","description":"The text of the reaction","type":"string"},"timestamp":{"title":"ReactionSender.timestamp","description":"The timestamp of the reaction","type":"number"},"orphan":{"title":"ReactionSender.orphan","type":"number"},"read":{"title":"ReactionSender.read","description":"If the reaction was seen/read","type":"boolean"},"t":{"title":"ReactionSender.t","description":"The timestamp of the reaction","type":"number"},"id":{"$ref":"#/components/schemas/MessageId","title":"ReactionSender.id","description":"The message ID of the reaction itself"},"isSendFailure":{"title":"ReactionSender.isSendFailure","type":"boolean"},"ack":{"title":"ReactionSender.ack","type":"number"}},"required":["parentMsgKey","senderUserJid","msgKey","reactionText","timestamp","orphan","read","id","isSendFailure"],"additionalProperties":false,"title":"ReactionSender","type":"object"},"PollOption":{"properties":{"name":{"title":"PollOption.name","type":"string"},"localId":{"title":"PollOption.localId","type":"number"}},"required":["name","localId"],"additionalProperties":false,"title":"PollOption","type":"object"},"PollData":{"properties":{"totalVotes":{"title":"PollData.totalVotes","description":"The total amount of votes recorded so far","type":"number"},"pollOptions":{"items":{"allOf":[{"$ref":"#/components/schemas/PollOption"},{"properties":{"count":{"title":"count","type":"number"}},"required":["count"],"additionalProperties":false,"type":"object"}]},"title":"PollData.pollOptions","description":"The poll options and their respective count of votes.","type":"array"},"votes":{"items":{"$ref":"#/components/schemas/PollVote","title":"PollData.votes.[]"},"title":"PollData.votes","description":"An arrray of vote objects","type":"array"},"pollMessage":{"$ref":"#/components/schemas/Message","title":"PollData.pollMessage","description":"The message object of the poll"}},"required":["totalVotes","pollOptions","votes","pollMessage"],"additionalProperties":false,"title":"PollData","type":"object"},"PollVote":{"properties":{"ack":{"title":"PollVote.ack","type":"number"},"id":{"title":"PollVote.id","description":"The message ID of this vote. For some reason this is different from the msgKey and includes exclamaition marks.","type":"string"},"msgKey":{"title":"PollVote.msgKey","description":"The message key of this vote","type":"string"},"parentMsgKey":{"title":"PollVote.parentMsgKey","description":"The Message ID of the original Poll message","type":"string"},"pollOptions":{"items":{"$ref":"#/components/schemas/PollOption","title":"PollVote.pollOptions.[]"},"title":"PollVote.pollOptions","description":"The original poll options available on the poll","type":"array"},"selectedOptionLocalIds":{"items":{"title":"PollVote.selectedOptionLocalIds.[]","type":"number"},"title":"PollVote.selectedOptionLocalIds","description":"The selected option IDs of the voter","type":"array"},"selectedOptionValues":{"items":{"title":"PollVote.selectedOptionValues.[]","type":"string"},"title":"PollVote.selectedOptionValues","description":"The selected option values by this voter","type":"array"},"sender":{"$ref":"#/components/schemas/ContactId","title":"PollVote.sender","description":"The contact ID of the voter"},"senderObj":{"$ref":"#/components/schemas/Contact","title":"PollVote.senderObj","description":"The contact object of the voter"},"senderTimestampMs":{"title":"PollVote.senderTimestampMs","description":"Timestamp of the vote","type":"number"},"stale":{"title":"PollVote.stale","type":"boolean"}},"required":["ack","id","msgKey","parentMsgKey","pollOptions","selectedOptionLocalIds","selectedOptionValues","sender","senderObj","senderTimestampMs","stale"],"additionalProperties":false,"title":"PollVote","type":"object"},"QuoteMap":{"additionalProperties":{"properties":{"body":{"title":"body","description":"The body of the message","type":"string"},"quotes":{"$ref":"#/components/schemas/MessageId","title":"quotes","description":"The message ID of the message that was quoted. Null if no message was quoted."}},"required":["body"],"additionalProperties":false,"type":"object"},"title":"QuoteMap","type":"object"},"MessageInfoInteraction":{"properties":{"id":{"$ref":"#/components/schemas/ContactId","title":"MessageInfoInteraction.id","description":"The contact ID of the contact that interacted with the message."},"t":{"title":"MessageInfoInteraction.t","description":"The timestamp of the interaction. You have to x 1000 to use in a JS Date object.","type":"number"}},"required":["id","t"],"additionalProperties":false,"title":"MessageInfoInteraction","type":"object"},"MessageInfo":{"properties":{"deliveryRemaining":{"title":"MessageInfo.deliveryRemaining","type":"number"},"playedRemaining":{"title":"MessageInfo.playedRemaining","type":"number"},"readRemaining":{"title":"MessageInfo.readRemaining","type":"number"},"delivery":{"items":{"$ref":"#/components/schemas/MessageInfoInteraction","title":"MessageInfo.delivery.[]"},"title":"MessageInfo.delivery","type":"array"},"read":{"items":{"$ref":"#/components/schemas/MessageInfoInteraction","title":"MessageInfo.read.[]"},"title":"MessageInfo.read","type":"array"},"played":{"items":{"$ref":"#/components/schemas/MessageInfoInteraction","title":"MessageInfo.played.[]"},"title":"MessageInfo.played","type":"array"},"id":{"$ref":"#/components/schemas/MessageId","title":"MessageInfo.id","description":"The ID of the message"}},"required":["deliveryRemaining","playedRemaining","readRemaining","delivery","read","played","id"],"additionalProperties":false,"title":"MessageInfo","type":"object"},"CustomProduct":{"properties":{"name":{"title":"CustomProduct.name","description":"The main title of the product. E.g:\n`BAVARIA — 35 SPORTS CRUISER (2006)`","type":"string"},"description":{"title":"CustomProduct.description","description":"The description of the product. This shows right under the price so it is useful for subscriptions/rentals. E.g:\n\n`(per day)\\n\\nCome and have a fantastic sailing adventure aboard our boat. \\nShe is a Bavaria 35 sports cruiser and is powered by 2 economical Volvo D6’s with Bravo 2 outdrives as well as a bow thruster. This Makes maneuvering very easy. She can accommodate up to 8 people for day charters and for overnight charters she can accommodate 4 in comfort in 2 cabins.`","type":"string"},"priceAmount1000":{"title":"CustomProduct.priceAmount1000","description":"The price amount multiplied by 1000. For example, for something costing `825` units of currency:\n`825000`","type":"number"},"currency":{"title":"CustomProduct.currency","description":"The [**ISO 4217**](https://en.wikipedia.org/wiki/ISO_4217) 3 letter currency code. E.g (Swedish krona)\n`SEK`","type":"string"},"url":{"title":"CustomProduct.url","description":"The URL of the product.\n\nNOTE: At the moment, the URL DOES NOT WORK. It shows up for the recipient but they will not be able to click it. As a rememdy, it is added as a reply to the product message.","type":"string"}},"required":["name","description","priceAmount1000","currency"],"additionalProperties":false,"title":"CustomProduct","type":"object"},"CartItem":{"properties":{"id":{"title":"CartItem.id","description":"Product ID","type":"string"},"name":{"title":"CartItem.name","description":"Product name","type":"string"},"qty":{"title":"CartItem.qty","description":"Amount of this item in the cart","type":"number"},"thumbnailId":{"title":"CartItem.thumbnailId","type":"string"},"thumbnailUrl":{"title":"CartItem.thumbnailUrl","description":"URL to .enc file of the thumbnail. Just change the filetype to .jpg to view the thumbnail","type":"string"}},"required":["id","name","qty","thumbnailId","thumbnailUrl"],"additionalProperties":false,"title":"CartItem","type":"object"},"Product":{"properties":{"id":{"title":"Product.id","description":"Product ID","type":"string"},"isHidden":{"title":"Product.isHidden","description":"`true` if the product is hidden from public view.","type":"boolean"},"catalogWid":{"title":"Product.catalogWid","description":"The id of the catalog in which this product is located.","type":"string"},"url":{"title":"Product.url","description":"The URL of the product.","type":"string"},"name":{"title":"Product.name","description":"The name of the product.","type":"string"},"description":{"title":"Product.description","description":"The description of the product.","type":"string"},"availability":{"anyOf":[{"title":"Product.availability","type":"number"},{"title":"Product.availability","enum":["unknown"],"type":"string"}],"title":"Product.availability","description":"The availiable quantity of this product.","default":"\"unknown\"`"},"reviewStatus":{"enum":["NO_REVIEW","PENDING","REJECTED","APPROVED","OUTDATED"],"title":"Product.reviewStatus","description":"The review status of the product","type":"string"},"imageCdnUrl":{"title":"Product.imageCdnUrl","description":"The url of the main image of the product.\n\nNOTE: If downloading manually, the filetype must be changed to .jpg to view the image.","type":"string"},"imageCount":{"title":"Product.imageCount","description":"The number of images of the product.","type":"number"},"additionalImageCdnUrl":{"items":{"title":"Product.additionalImageCdnUrl.[]","type":"string"},"title":"Product.additionalImageCdnUrl","description":"Array of URLs of the other images of the product. Does not include the main image.","type":"array"},"priceAmount1000":{"title":"Product.priceAmount1000","description":"The price of the product in 1000 units.","type":"number"},"retailerId":{"title":"Product.retailerId","description":"The custom id of the product.","type":"string"},"t":{"title":"Product.t","description":"The timestamp when the product was created / 1000","type":"number"},"currency":{"title":"Product.currency","description":"The [**ISO 4217**](https://en.wikipedia.org/wiki/ISO_4217) 3 letter currency code. E.g (Swedish krona)\n`SEK`","type":"string"}},"required":["id","currency"],"additionalProperties":false,"title":"Product","type":"object"},"Order":{"properties":{"id":{"title":"Order.id","description":"Order ID","type":"string"},"createdAt":{"title":"Order.createdAt","description":"epoch ts divided by 1000","type":"number"},"currency":{"title":"Order.currency","description":"The [**ISO 4217**](https://en.wikipedia.org/wiki/ISO_4217) 3 letter currency code. E.g (Swedish krona)\n`SEK`","type":"string"},"products":{"items":{"$ref":"#/components/schemas/CartItem","title":"Order.products.[]"},"title":"Order.products","description":"An array of items in the cart","type":"array"},"sellerJid":{"title":"Order.sellerJid","type":"string"},"subtotal":{"title":"Order.subtotal"},"total":{"title":"Order.total"},"message":{"$ref":"#/components/schemas/Message","title":"Order.message","description":"The message object associated with the order. Only populated in `onOrder` callback."}},"required":["id","createdAt","currency","products","sellerJid","subtotal","total"],"additionalProperties":false,"title":"Order","type":"object"},"Reaction":{"properties":{"aggregateEmoji":{"title":"Reaction.aggregateEmoji","description":"The aggregate emoji used for the reaction.","type":"string"},"id":{"title":"Reaction.id","description":"The identifier of the reaction","type":"string"},"hasReactionByMe":{"title":"Reaction.hasReactionByMe","description":"If the reaction is also sent by the host account","type":"boolean"},"senders":{"items":{"$ref":"#/components/schemas/ReactionRecord","title":"Reaction.senders.[]"},"title":"Reaction.senders","description":"The senders of this spefcific reaction","type":"array"}},"required":["aggregateEmoji","id","hasReactionByMe","senders"],"additionalProperties":false,"title":"Reaction","description":"A reaction is identified the specific emoji.","type":"object"},"ReactionRecord":{"properties":{"ack":{"$ref":"#/components/schemas/MessageAck","title":"ReactionRecord.ack","description":"The acknowledgement of the reaction"},"id":{"title":"ReactionRecord.id","description":"The ID of the reaction","type":"string"},"msgKey":{"title":"ReactionRecord.msgKey","type":"string"},"parentMsgKey":{"title":"ReactionRecord.parentMsgKey","type":"string"},"orphan":{"title":"ReactionRecord.orphan","type":"number"},"reactionText":{"title":"ReactionRecord.reactionText","description":"The text of the reaction","type":"string"},"read":{"title":"ReactionRecord.read","description":"If the reaction has been read","type":"boolean"},"senderUserJid":{"$ref":"#/components/schemas/ContactId","title":"ReactionRecord.senderUserJid","description":"The ID of the reaction sender"},"timestamp":{"title":"ReactionRecord.timestamp","description":"The timestamp of the reaction","type":"number"}},"required":["ack","id","msgKey","parentMsgKey","orphan","reactionText","read","senderUserJid","timestamp"],"additionalProperties":false,"title":"ReactionRecord","description":"The specific reaction by a user","type":"object"},"ReactionEvent":{"properties":{"message":{"$ref":"#/components/schemas/Message","title":"ReactionEvent.message","description":"The message being reacted to"},"reactionByMe":{"$ref":"#/components/schemas/Reaction","title":"ReactionEvent.reactionByMe","description":"The reaction sent by the host account"},"reactions":{"items":{"$ref":"#/components/schemas/Reaction","title":"ReactionEvent.reactions.[]"},"title":"ReactionEvent.reactions","description":"An array of all reactions","type":"array"},"type":{"enum":["add","change"],"title":"ReactionEvent.type","description":"The type of the reaction event.","type":"string"}},"required":["message","reactionByMe","reactions","type"],"additionalProperties":false,"title":"ReactionEvent","description":"Emitted by onReaction","type":"object"},"SessionInfo":{"properties":{"WA_VERSION":{"title":"SessionInfo.WA_VERSION","type":"string"},"PAGE_UA":{"title":"SessionInfo.PAGE_UA","type":"string"},"WA_AUTOMATE_VERSION":{"title":"SessionInfo.WA_AUTOMATE_VERSION","type":"string"},"BROWSER_VERSION":{"title":"SessionInfo.BROWSER_VERSION","type":"string"},"LAUNCH_TIME_MS":{"title":"SessionInfo.LAUNCH_TIME_MS","type":"number"},"NUM":{"title":"SessionInfo.NUM","type":"string"},"OS":{"title":"SessionInfo.OS","type":"string"},"START_TS":{"title":"SessionInfo.START_TS","type":"number"},"PHONE_VERSION":{"title":"SessionInfo.PHONE_VERSION","type":"string"},"NUM_HASH":{"title":"SessionInfo.NUM_HASH","type":"string"},"PATCH_HASH":{"title":"SessionInfo.PATCH_HASH","type":"string"},"OW_KEY":{"title":"SessionInfo.OW_KEY","type":"string"},"INSTANCE_ID":{"title":"SessionInfo.INSTANCE_ID","type":"string"},"RAM_INFO":{"title":"SessionInfo.RAM_INFO","type":"string"},"PPTR_VERSION":{"title":"SessionInfo.PPTR_VERSION","type":"string"},"LATEST_VERSION":{"title":"SessionInfo.LATEST_VERSION","type":"boolean"},"CLI":{"title":"SessionInfo.CLI","type":"boolean"},"ACC_TYPE":{"enum":["PERSONAL","BUSINESS"],"title":"SessionInfo.ACC_TYPE","type":"string"}},"required":["WA_VERSION","PAGE_UA","WA_AUTOMATE_VERSION","BROWSER_VERSION"],"additionalProperties":false,"title":"SessionInfo","type":"object"},"HealthCheck":{"properties":{"queuedMessages":{"title":"HealthCheck.queuedMessages","description":"The number of messages queued up in the browser. Messages can start being queued up due to the web app awaiting a connection with the host device.\n\nHealthy: 0","type":"number"},"state":{"$ref":"#/components/schemas/STATE","title":"HealthCheck.state","description":"The state of the web app.\n\nHealthy: 'CONNECTED'"},"isPhoneDisconnected":{"title":"HealthCheck.isPhoneDisconnected","description":"Whether or not the \"Phone is disconnected\" message is showing within the web app.\n\nHealthy: `false`","type":"boolean"},"isHere":{"title":"HealthCheck.isHere","description":"Returns `true` if \"Use Here\" button is not detected\n\nHealthy: `true`","type":"boolean"},"wapiInjected":{"title":"HealthCheck.wapiInjected","description":"Returns `true` if the `WAPI` object is detected.\n\nHealthy: `true`","type":"boolean"},"online":{"title":"HealthCheck.online","description":"Result of `window.navigator.onLine`\n\nHealthy: `true`","type":"boolean"},"tryingToReachPhone":{"title":"HealthCheck.tryingToReachPhone","description":"Returns `true` if \"trying to reach phone\" dialog is detected\n\nHealthy: `false`","type":"boolean"},"retryingIn":{"title":"HealthCheck.retryingIn","description":"Returns the number of seconds the \"Retrying in ...\" dialog is indicating. If the dialog is not showing, it will return `0`.\n\nHealthy: `0`","type":"number"},"batteryLow":{"title":"HealthCheck.batteryLow","description":"Returns `true` if \"Phone battery low\" message is detected\n\nHealthy: `false`","type":"boolean"}},"additionalProperties":false,"title":"HealthCheck","type":"object"},"expressMiddleware":{"title":"expressMiddleware"},"ChatwootConfig":{"properties":{"chatwootUrl":{"title":"ChatwootConfig.chatwootUrl","description":"The URL of the chatwoot inbox. If you want this integration to create & manage the inbox for you, you can omit the inbox part.","type":"string"},"chatwootApiAccessToken":{"title":"ChatwootConfig.chatwootApiAccessToken","description":"The API access token which you can get from your account menu.","type":"string"},"apiHost":{"title":"ChatwootConfig.apiHost","description":"The API host which will be used as the webhook address in the Chatwoot inbox.","type":"string"},"host":{"title":"ChatwootConfig.host","description":"Similar to apiHost","type":"string"},"https":{"title":"ChatwootConfig.https","description":"Whether or not to use https for the webhook address","type":"boolean"},"cert":{"title":"ChatwootConfig.cert","description":"The certificate for https","type":"string"},"privkey":{"title":"ChatwootConfig.privkey","description":"The private key for https","type":"string"},"key":{"title":"ChatwootConfig.key","description":"The API key used to secure the instance webhook address","type":"string"},"forceUpdateCwWebhook":{"title":"ChatwootConfig.forceUpdateCwWebhook","description":"Whether or not to update the webhook address in the Chatwoot inbox on launch","type":"boolean"},"port":{"title":"ChatwootConfig.port","description":"port","type":"number"},"client":{"$ref":"#/components/schemas/Client","title":"ChatwootConfig.client"}},"required":["chatwootUrl","chatwootApiAccessToken","apiHost","host","cert","privkey","port"],"additionalProperties":false,"title":"ChatwootConfig","type":"object"},"MultiSessionConfig":{"properties":{"port":{"title":"MultiSessionConfig.port","type":"number"},"host":{"title":"MultiSessionConfig.host","type":"string"},"enableCors":{"title":"MultiSessionConfig.enableCors","type":"boolean"},"defaultSessionConfig":{"title":"MultiSessionConfig.defaultSessionConfig"},"maxSessions":{"title":"MultiSessionConfig.maxSessions","type":"number"}},"additionalProperties":false,"title":"MultiSessionConfig","description":"多Session服务器配置","type":"object"},"cliFlags":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"}]},"title":"cliFlags","type":"object"},"SessionInstance":{"properties":{"client":{"anyOf":[{"$ref":"#/components/schemas/Client","title":"SessionInstance.client"},{"title":"SessionInstance.client","nullable":true}],"title":"SessionInstance.client"},"config":{"$ref":"#/components/schemas/ConfigObject","title":"SessionInstance.config"},"status":{"enum":["initializing","qr_ready","authenticated","ready","failed","terminated"],"title":"SessionInstance.status","type":"string"},"createdAt":{"$ref":"#/components/schemas/Date","title":"SessionInstance.createdAt"},"lastActivity":{"$ref":"#/components/schemas/Date","title":"SessionInstance.lastActivity"},"qrCode":{"title":"SessionInstance.qrCode","type":"string"},"qrCodeUrl":{"title":"SessionInstance.qrCodeUrl","type":"string"}},"required":["client","config","status","createdAt","lastActivity"],"additionalProperties":false,"title":"SessionInstance","type":"object"},"ConfigLogTransport":{"properties":{"type":{"enum":["syslog","console","file","ev"],"title":"ConfigLogTransport.type","description":"The type of winston transport. At the moment only `file`, `console`, `ev` and `syslog` are supported.","type":"string"},"options":{"title":"ConfigLogTransport.options","description":"The options for the transport. Generally only required for syslog but you can use this to override default options for other types of transports."},"done":{"title":"ConfigLogTransport.done","description":"If the transport has already been added to the logger. The logging set up command handles this for you.","type":"boolean"}},"required":["type"],"additionalProperties":false,"title":"ConfigLogTransport","type":"object"},"CollectorOptions":{"properties":{"maxProcessed":{"title":"CollectorOptions.maxProcessed","description":"The maximum amount of items to process","type":"number"},"max":{"title":"CollectorOptions.max","description":"The maximum amount of items to collect","type":"number"},"time":{"title":"CollectorOptions.time","description":"Max time to wait for items in milliseconds","type":"number"},"idle":{"title":"CollectorOptions.idle","description":"Max time allowed idle","type":"number"},"dispose":{"title":"CollectorOptions.dispose","description":"Whether to dispose data when it's deleted","type":"boolean"}},"additionalProperties":false,"title":"CollectorOptions","description":"Options to be applied to the collector.","type":"object"},"AwaitMessagesOptions":{"properties":{"errors":{"items":{"title":"AwaitMessagesOptions.errors.[]","type":"string"},"title":"AwaitMessagesOptions.errors","description":"An array of \"reasons\" that would result in the awaitMessages command to throw an error.","type":"array"}},"additionalProperties":false,"title":"AwaitMessagesOptions","type":"object"},"CurrentDialogProps":{"additionalProperties":{},"title":"CurrentDialogProps","type":"object"},"DialogState":{"properties":{"currentStep":{"title":"DialogState.currentStep","type":"number"},"currentProps":{"title":"DialogState.currentProps"},"lastInput":{"title":"DialogState.lastInput"},"isComplete":{"title":"DialogState.isComplete","type":"boolean"},"isError":{"title":"DialogState.isError","type":"boolean"},"errorMessage":{"title":"DialogState.errorMessage","type":"string"}},"required":["currentStep","currentProps","lastInput","isComplete","isError","errorMessage"],"additionalProperties":false,"title":"DialogState","type":"object"},"DialogTemplate":{"properties":{"\"dialogId\"":{"title":"DialogTemplate.\"dialogId\"","type":"string"},"\"privateOnly\"":{"title":"DialogTemplate.\"privateOnly\"","type":"boolean"},"\"identifier\"":{"title":"DialogTemplate.\"identifier\"","type":"string"},"\"successMessage\"":{"title":"DialogTemplate.\"successMessage\"","type":"string"},"\"startMessage\"":{"title":"DialogTemplate.\"startMessage\"","type":"string"},"\"properties\"":{"additionalProperties":{"$ref":"#/components/schemas/DialogProperty"},"title":"DialogTemplate.\"properties\"","type":"object"}},"required":["\"dialogId\"","\"privateOnly\"","\"identifier\"","\"successMessage\"","\"startMessage\"","\"properties\""],"additionalProperties":false,"title":"DialogTemplate","type":"object"},"CheckFunction":{"title":"CheckFunction"},"DialogProperty":{"properties":{"\"order\"":{"title":"DialogProperty.\"order\"","type":"number"},"\"key\"":{"title":"DialogProperty.\"key\"","type":"string"},"\"prompt\"":{"title":"DialogProperty.\"prompt\"","type":"string"},"\"type\"":{"title":"DialogProperty.\"type\"","type":"string"},"\"skipCondition\"":{"$ref":"#/components/schemas/CheckFunction","title":"DialogProperty.\"skipCondition\""},"\"options\"":{"anyOf":[{"items":{"$ref":"#/components/schemas/DialogButtons","title":"DialogProperty.\"options\".[]"},"title":"DialogProperty.\"options\".[]","type":"array"},{"items":{"$ref":"#/components/schemas/DialogListMessageSection","title":"DialogProperty.\"options\".[]"},"title":"DialogProperty.\"options\".[]","type":"array"}],"title":"DialogProperty.\"options\""},"\"validation\"":{"items":{"$ref":"#/components/schemas/DialogValidation","title":"DialogProperty.\"validation\".[]"},"title":"DialogProperty.\"validation\"","type":"array"}},"required":["\"order\"","\"key\"","\"prompt\"","\"type\"","\"validation\""],"additionalProperties":false,"title":"DialogProperty","type":"object"},"DialogButtons":{"properties":{"label":{"title":"DialogButtons.label","type":"string"},"value":{"title":"DialogButtons.value","type":"string"}},"required":["label","value"],"additionalProperties":false,"title":"DialogButtons","type":"object"},"DialogListMessageSection":{"properties":{"title":{"title":"DialogListMessageSection.title","type":"string"},"rows":{"items":{"$ref":"#/components/schemas/DialogListMessageRow","title":"DialogListMessageSection.rows.[]"},"title":"DialogListMessageSection.rows","type":"array"}},"required":["title","rows"],"additionalProperties":false,"title":"DialogListMessageSection","type":"object"},"DialogListMessageRow":{"properties":{"title":{"title":"DialogListMessageRow.title","type":"string"},"description":{"title":"DialogListMessageRow.description","type":"string"},"value":{"title":"DialogListMessageRow.value","type":"string"}},"required":["title","description","value"],"additionalProperties":false,"title":"DialogListMessageRow","type":"object"},"DialogValidation":{"properties":{"\"type\"":{"$ref":"#/components/schemas/ValidationType","title":"DialogValidation.\"type\""},"\"value\"":{"anyOf":[{"title":"DialogValidation.\"value\"","type":"string"},{"$ref":"#/components/schemas/CheckFunction","title":"DialogValidation.\"value\""}],"title":"DialogValidation.\"value\""},"\"errorMessage\"":{"title":"DialogValidation.\"errorMessage\"","type":"string"}},"required":["\"type\"","\"value\"","\"errorMessage\""],"additionalProperties":false,"title":"DialogValidation","type":"object"},"MessagePreProcessor":{"title":"MessagePreProcessor","description":"A function that takes a message and returns a message.\n@param The message to be processed\n@param The client that received the message\n@param Whether the message has already been processed by another preprocessor. (This is useful in cases where you want to mutate the message for both onMessage and onAnyMessage events but only want to do the actual process, like uploading to s3, once.)\n@param The source of the message. This is useful for knowing if the message is from onMessage or onAnyMessage. Only processing one source will prevent duplicate processing."},"MPConfigType":{"anyOf":[{"$ref":"#/components/schemas/PREPROCESSORS","title":"MPConfigType"},{"$ref":"#/components/schemas/MessagePreProcessor","title":"MPConfigType"},{"items":{"anyOf":[{"$ref":"#/components/schemas/PREPROCESSORS","title":"MPConfigType.[]"},{"$ref":"#/components/schemas/MessagePreProcessor","title":"MPConfigType.[]"}],"title":"MPConfigType.[]"},"title":"MPConfigType.[]","type":"array"}],"title":"MPConfigType","description":"The actual type for [config.messagePreprocessor](/docs/api/interfaces/api_model_config.ConfigObject#messagepreprocessor)"}}
diff --git a/debug_proxy_config.js b/debug_proxy_config.js
new file mode 100644
index 0000000000..9d80855379
--- /dev/null
+++ b/debug_proxy_config.js
@@ -0,0 +1,93 @@
+#!/usr/bin/env node
+
+/**
+ * 调试代理配置的脚本
+ * 用于测试代理配置的保存和读取是否正常
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+function debugProxyConfig(sessionId) {
+ const sessionDir = path.join('./sessions', sessionId);
+ const configPath = path.join(sessionDir, 'config.json');
+
+ console.log(`\n=== 调试会话 ${sessionId} 的代理配置 ===`);
+ console.log(`会话目录: ${sessionDir}`);
+ console.log(`配置文件: ${configPath}`);
+
+ if (!fs.existsSync(configPath)) {
+ console.log('❌ 配置文件不存在');
+ return;
+ }
+
+ try {
+ const configContent = fs.readFileSync(configPath, 'utf-8');
+ const config = JSON.parse(configContent);
+
+ console.log('\n📄 配置文件内容:');
+ console.log(JSON.stringify(config, null, 2));
+
+ console.log('\n🔍 代理配置分析:');
+ if (config.proxyServerCredentials) {
+ console.log('✅ 找到代理配置:');
+ console.log(` 地址: ${config.proxyServerCredentials.address}`);
+ console.log(` 协议: ${config.proxyServerCredentials.protocol || 'http'}`);
+ console.log(` 用户名: ${config.proxyServerCredentials.username || '无'}`);
+ console.log(` 密码: ${config.proxyServerCredentials.password ? '已设置' : '无'}`);
+ } else {
+ console.log('❌ 未找到代理配置');
+ }
+
+ console.log(`\n🔧 原生代理模式: ${config.useNativeProxy ? '启用' : '禁用'}`);
+
+ } catch (error) {
+ console.log('❌ 读取配置文件失败:', error.message);
+ }
+}
+
+function listAllSessions() {
+ const sessionsDir = './sessions';
+
+ console.log('\n=== 所有会话列表 ===');
+
+ if (!fs.existsSync(sessionsDir)) {
+ console.log('❌ sessions目录不存在');
+ return [];
+ }
+
+ const sessionFolders = fs.readdirSync(sessionsDir).filter(file =>
+ fs.statSync(path.join(sessionsDir, file)).isDirectory()
+ );
+
+ console.log(`找到 ${sessionFolders.length} 个会话:`);
+ sessionFolders.forEach(sessionId => {
+ console.log(` - ${sessionId}`);
+ });
+
+ return sessionFolders;
+}
+
+// 主函数
+function main() {
+ const args = process.argv.slice(2);
+
+ if (args.length === 0) {
+ console.log('用法:');
+ console.log(' node debug_proxy_config.js # 调试特定会话');
+ console.log(' node debug_proxy_config.js --all # 调试所有会话');
+ return;
+ }
+
+ if (args[0] === '--all') {
+ const sessions = listAllSessions();
+ sessions.forEach(sessionId => {
+ debugProxyConfig(sessionId);
+ });
+ } else {
+ const sessionId = args[0];
+ debugProxyConfig(sessionId);
+ }
+}
+
+main();
\ No newline at end of file
diff --git a/debug_qr.js b/debug_qr.js
new file mode 100644
index 0000000000..1da064e6f0
--- /dev/null
+++ b/debug_qr.js
@@ -0,0 +1,39 @@
+const { SessionManager } = require('./dist/controllers/SessionManager.js');
+const { DEFAULT_MULTI_SESSION_CONFIG } = require('./dist/config/multiSessionConfig.js');
+
+console.log('Starting QR code debug test...');
+
+async function testQRCodeGeneration() {
+ try {
+ console.log('1. Creating SessionManager...');
+ const sessionManager = new SessionManager(DEFAULT_MULTI_SESSION_CONFIG);
+
+ console.log('2. Creating session with waitForQR=true...');
+ const sessionId = 'debug_test_' + Date.now();
+
+ const result = await sessionManager.createSession(sessionId, {
+ multiDevice: true,
+ headless: true
+ }, true); // waitForQR = true
+
+ console.log('3. Session creation result:', result);
+
+ if (result.success && result.qrCode) {
+ console.log('SUCCESS: QR code received!');
+ console.log('QR code length:', result.qrCode.length);
+ console.log('QR code preview:', result.qrCode.substring(0, 100) + '...');
+ } else {
+ console.log('FAILED: No QR code received');
+ }
+
+ // Cleanup
+ await sessionManager.removeSession(sessionId);
+ await sessionManager.cleanup();
+
+ } catch (error) {
+ console.error('Error during test:', error);
+ process.exit(1);
+ }
+}
+
+testQRCodeGeneration();
\ No newline at end of file
diff --git a/demo/server.js b/demo/server.js
new file mode 100644
index 0000000000..6941d0c937
--- /dev/null
+++ b/demo/server.js
@@ -0,0 +1,66 @@
+"use strict";
+/**
+ * This example shows how to use client.registerWebhook to easily set up webhooks. You can see the valid webhooks here:
+ * https://open-wa.github.io/wa-automate-nodejs/enums/SimpleListener.html
+ */
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+//Please see these docs: https://open-wa.github.io/wa-automate-nodejs/classes/client.html#middleware
+// import { create, Client, SimpleListener } from '@open-wa/wa-automate';
+var index_1 = require("../src/index");
+var express = require('express');
+var app = express();
+app.use(express.json({ limit: '200mb' })); //add the limit option so we can send base64 data through the api
+var PORT = 8082;
+//Create your webhook here: https://webhook.site/
+var WEBHOOK_ADDRESS = 'PASTE_WEBHOOK_DOT_SITE_UNIQUE_URL_HERE';
+(0, index_1.create)({ sessionId: 'session1',
+ proxyServerCredentials: {
+ address: 'socks5://88.216.251.99:5432',
+ username: 'c6e10',
+ password: 'p2lav77j'
+ }
+})
+ .then(function (client) { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ app.use(client.middleware());
+ Object.keys(index_1.SimpleListener).map(function (eventKey) { return client.registerWebhook(index_1.SimpleListener[eventKey], WEBHOOK_ADDRESS); });
+ app.listen(PORT, function () { return console.log("\n\u2022 Listening on port ".concat(PORT, "!")); });
+ return [2 /*return*/];
+ });
+}); })["catch"](function (e) { return console.log('Error', e.message); });
diff --git a/demo/server.ts b/demo/server.ts
index 68b34afc7e..2680d3cf94 100644
--- a/demo/server.ts
+++ b/demo/server.ts
@@ -16,7 +16,13 @@ const PORT = 8082;
//Create your webhook here: https://webhook.site/
const WEBHOOK_ADDRESS = 'PASTE_WEBHOOK_DOT_SITE_UNIQUE_URL_HERE'
-create({ sessionId:'session1'})
+create({ sessionId:'session1',
+ proxyServerCredentials: {
+ address: 'socks5://88.216.251.99:5432',
+ username: 'c6e10',
+ password: 'p2lav77j'
+ }
+})
.then(async (client:Client) => {
app.use(client.middleware());
Object.keys(SimpleListener).map(eventKey=>client.registerWebhook(SimpleListener[eventKey],WEBHOOK_ADDRESS))
diff --git a/package-lock.json b/package-lock.json
index 316984fda9..0b6a532966 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -41,6 +41,7 @@
"get-port": "^5.1.1",
"hasha": "^5.2.0",
"helmet": "^5.1.1",
+ "http-proxy-agent": "^7.0.2",
"image-type": "^4.1.0",
"is-url-superb": "^5.0.0",
"json5": "^2.2.0",
@@ -70,6 +71,7 @@
"smashah-puppeteer-page-proxy": "^1.2.8",
"socket.io": "^4.5.4",
"socket.io-client": "^4.5.4",
+ "socks-proxy-agent": "^8.0.5",
"spinnies": "^0.5.1",
"swagger-stats": "^0.99.1",
"swagger-ui-dist": "^4.1.3",
@@ -84,7 +86,7 @@
"update-notifier": "^5.0.0",
"uuid": "^9.0.0",
"uuid-apikey": "^1.5.3",
- "winston": "^3.6.0",
+ "winston": "^3.17.0",
"winston-daily-rotate-file": "^4.5.5",
"winston-syslog": "^2.5.0",
"xmlbuilder2": "^3.0.2"
@@ -114,6 +116,7 @@
"command-line-args": "^5.1.1",
"docusaurus": "^1.14.7",
"eslint": "^8.1.0",
+ "express-list-routes": "^1.3.1",
"husky": "^7.0.0",
"line-reader": "^0.4.0",
"marked": "^4.0.10",
@@ -2151,9 +2154,10 @@
"integrity": "sha512-4qA9sNtQZCY02MeLjTP+O9LfY9T6d2ajYS7whyvgBklJzWzLUNbJp940ClxK64hbDPqag9AQZt45beEAGcSYYA=="
},
"node_modules/@colors/colors": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
- "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "license": "MIT",
"engines": {
"node": ">=0.1.90"
}
@@ -2811,6 +2815,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "license": "MIT",
"engines": {
"node": ">= 6"
}
@@ -3163,6 +3168,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+ "license": "MIT"
+ },
"node_modules/@types/winston-syslog": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/winston-syslog/-/winston-syslog-2.4.0.tgz",
@@ -9993,6 +10004,13 @@
"node": ">=8.9.0"
}
},
+ "node_modules/express-list-routes": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/express-list-routes/-/express-list-routes-1.3.1.tgz",
+ "integrity": "sha512-HC8gpFjmeoJv1f2vJVBJHThA9XXVSOzIQd0I6WvB9G/E7Yy6f8AnFkam7GJRz/+JJQG3fX49eXWhVqjdNj32dw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/express-robots-txt": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/express-robots-txt/-/express-robots-txt-1.0.0.tgz",
@@ -12103,24 +12121,34 @@
}
},
"node_modules/http-proxy-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
- "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "license": "MIT",
"dependencies": {
- "@tootallnate/once": "1",
- "agent-base": "6",
- "debug": "4"
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
},
"engines": {
- "node": ">= 6"
+ "node": ">= 14"
+ }
+ },
+ "node_modules/http-proxy-agent/node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
}
},
"node_modules/http-proxy-agent/node_modules/debug": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
- "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
"dependencies": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@@ -12132,9 +12160,10 @@
}
},
"node_modules/http-proxy-agent/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
},
"node_modules/http-proxy-response-rewrite": {
"version": "0.0.1",
@@ -12188,15 +12217,25 @@
}
},
"node_modules/https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
"dependencies": {
- "agent-base": "6",
+ "agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
- "node": ">= 6"
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent/node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
}
},
"node_modules/https-proxy-agent/node_modules/debug": {
@@ -14341,15 +14380,20 @@
}
},
"node_modules/logform": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz",
- "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==",
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+ "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+ "license": "MIT",
"dependencies": {
- "@colors/colors": "1.5.0",
+ "@colors/colors": "1.6.0",
+ "@types/triple-beam": "^1.3.2",
"fecha": "^4.2.0",
"ms": "^2.1.1",
"safe-stable-stringify": "^2.3.1",
"triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
}
},
"node_modules/logform/node_modules/ms": {
@@ -16247,52 +16291,12 @@
}
}
},
- "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.0.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/pac-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": {
- "version": "8.0.4",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
- "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.1",
- "debug": "^4.3.4",
- "socks": "^2.8.3"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/pac-resolver": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
@@ -18525,32 +18529,6 @@
}
}
},
- "node_modules/proxy-agent/node_modules/http-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/proxy-agent/node_modules/https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.0.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/proxy-agent/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
@@ -18566,20 +18544,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/proxy-agent/node_modules/socks-proxy-agent": {
- "version": "8.0.4",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
- "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.1",
- "debug": "^4.3.4",
- "socks": "^2.8.3"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -19775,9 +19739,10 @@
}
},
"node_modules/readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -21321,6 +21286,23 @@
"node": ">=8"
}
},
+ "node_modules/smashah-puppeteer-page-proxy/node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
"node_modules/smashah-puppeteer-page-proxy/node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@@ -21364,6 +21346,33 @@
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
},
+ "node_modules/smashah-puppeteer-page-proxy/node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/smashah-puppeteer-page-proxy/node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/smashah-puppeteer-page-proxy/node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -21397,6 +21406,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/smashah-puppeteer-page-proxy/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
"node_modules/smashah-puppeteer-page-proxy/node_modules/normalize-url": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
@@ -21427,6 +21442,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/smashah-puppeteer-page-proxy/node_modules/socks-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^6.0.2",
+ "debug": "4",
+ "socks": "^2.3.3"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/snake-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
@@ -21710,24 +21739,35 @@
}
},
"node_modules/socks-proxy-agent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz",
- "integrity": "sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA==",
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+ "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+ "license": "MIT",
"dependencies": {
- "agent-base": "6",
- "debug": "4",
- "socks": "^2.3.3"
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
},
"engines": {
- "node": ">= 6"
+ "node": ">= 14"
+ }
+ },
+ "node_modules/socks-proxy-agent/node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
}
},
"node_modules/socks-proxy-agent/node_modules/debug": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
- "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
"dependencies": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@@ -21739,9 +21779,10 @@
}
},
"node_modules/socks-proxy-agent/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
},
"node_modules/sort-keys": {
"version": "2.0.0",
@@ -24607,20 +24648,22 @@
}
},
"node_modules/winston": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz",
- "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==",
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
+ "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
+ "license": "MIT",
"dependencies": {
+ "@colors/colors": "^1.6.0",
"@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3",
"is-stream": "^2.0.0",
- "logform": "^2.4.0",
+ "logform": "^2.7.0",
"one-time": "^1.0.0",
"readable-stream": "^3.4.0",
"safe-stable-stringify": "^2.3.1",
"stack-trace": "0.0.x",
"triple-beam": "^1.3.0",
- "winston-transport": "^4.5.0"
+ "winston-transport": "^4.9.0"
},
"engines": {
"node": ">= 12.0.0"
@@ -24661,16 +24704,17 @@
}
},
"node_modules/winston-transport": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
- "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+ "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+ "license": "MIT",
"dependencies": {
- "logform": "^2.3.2",
- "readable-stream": "^3.6.0",
+ "logform": "^2.7.0",
+ "readable-stream": "^3.6.2",
"triple-beam": "^1.3.0"
},
"engines": {
- "node": ">= 6.4.0"
+ "node": ">= 12.0.0"
}
},
"node_modules/word-wrap": {
diff --git a/package.json b/package.json
index 93a4d7d440..d96990dd3e 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
"command-line-args": "^5.1.1",
"docusaurus": "^1.14.7",
"eslint": "^8.1.0",
+ "express-list-routes": "^1.3.1",
"husky": "^7.0.0",
"line-reader": "^0.4.0",
"marked": "^4.0.10",
@@ -134,6 +135,7 @@
"get-port": "^5.1.1",
"hasha": "^5.2.0",
"helmet": "^5.1.1",
+ "http-proxy-agent": "^7.0.2",
"image-type": "^4.1.0",
"is-url-superb": "^5.0.0",
"json5": "^2.2.0",
@@ -163,6 +165,7 @@
"smashah-puppeteer-page-proxy": "^1.2.8",
"socket.io": "^4.5.4",
"socket.io-client": "^4.5.4",
+ "socks-proxy-agent": "^8.0.5",
"spinnies": "^0.5.1",
"swagger-stats": "^0.99.1",
"swagger-ui-dist": "^4.1.3",
@@ -177,7 +180,7 @@
"update-notifier": "^5.0.0",
"uuid": "^9.0.0",
"uuid-apikey": "^1.5.3",
- "winston": "^3.6.0",
+ "winston": "^3.17.0",
"winston-daily-rotate-file": "^4.5.5",
"winston-syslog": "^2.5.0",
"xmlbuilder2": "^3.0.2"
diff --git a/screenshot_dadasda_1752774375430.png b/screenshot_dadasda_1752774375430.png
new file mode 100644
index 0000000000..ccfa050653
Binary files /dev/null and b/screenshot_dadasda_1752774375430.png differ
diff --git a/screenshot_dadasdad2312312_1752774375431.png b/screenshot_dadasdad2312312_1752774375431.png
new file mode 100644
index 0000000000..aa02ba8ec6
Binary files /dev/null and b/screenshot_dadasdad2312312_1752774375431.png differ
diff --git a/screenshot_dasdasdasda_1752774375431.png b/screenshot_dasdasdasda_1752774375431.png
new file mode 100644
index 0000000000..b0e070312c
Binary files /dev/null and b/screenshot_dasdasdasda_1752774375431.png differ
diff --git a/screenshot_dasdasdasdadasdas_1752774375431.png b/screenshot_dasdasdasdadasdas_1752774375431.png
new file mode 100644
index 0000000000..aa02ba8ec6
Binary files /dev/null and b/screenshot_dasdasdasdadasdas_1752774375431.png differ
diff --git a/sessions/827test/config.json b/sessions/827test/config.json
new file mode 100644
index 0000000000..ed4163d358
--- /dev/null
+++ b/sessions/827test/config.json
@@ -0,0 +1,16 @@
+{
+ "sessionId": "827test",
+ "qrLogSkip": true,
+ "qrTimeout": 120,
+ "sessionDataPath": "./sessions/827test",
+ "port": 8080,
+ "useNativeProxy": true,
+ "multiDevice": true,
+ "headless": true,
+ "proxyServerCredentials": {
+ "protocol": "http",
+ "address": "80.71.154.181:5432",
+ "username": "g3285",
+ "password": "xfox92zy"
+ }
+}
\ No newline at end of file
diff --git a/simple_test.js b/simple_test.js
new file mode 100644
index 0000000000..0c0f403c29
--- /dev/null
+++ b/simple_test.js
@@ -0,0 +1,42 @@
+const { create, ev } = require('./dist/index.js');
+
+console.log('Starting simple QR test...');
+
+let qrReceived = false;
+
+// 监听QR码
+ev.on('qr.**', (qrcode) => {
+ console.log('QR CODE RECEIVED!');
+ console.log('Length:', qrcode.length);
+ console.log('Preview:', qrcode.substring(0, 100));
+ qrReceived = true;
+
+ // 保存到文件验证
+ const fs = require('fs');
+ const imageBuffer = Buffer.from(qrcode.replace('data:image/png;base64,',''), 'base64');
+ fs.writeFileSync('test_qr.png', imageBuffer);
+ console.log('QR code saved to test_qr.png');
+
+ process.exit(0);
+});
+
+// 设置超时
+setTimeout(() => {
+ if (!qrReceived) {
+ console.log('TIMEOUT: No QR code received in 30 seconds');
+ process.exit(1);
+ }
+}, 30000);
+
+// 创建client
+create({
+ sessionId: 'simple_test',
+ multiDevice: true,
+ headless: true,
+ qrLogSkip: true
+}).then(() => {
+ console.log('Client created, waiting for QR...');
+}).catch(error => {
+ console.error('Error creating client:', error);
+ process.exit(1);
+});
\ No newline at end of file
diff --git a/src/api/Client.js b/src/api/Client.js
new file mode 100644
index 0000000000..8f025660e4
--- /dev/null
+++ b/src/api/Client.js
@@ -0,0 +1,6343 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+var __rest = (this && this.__rest) || function (s, e) {
+ var t = {};
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
+ t[p] = s[p];
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
+ t[p[i]] = s[p[i]];
+ }
+ return t;
+};
+var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+ if (ar || !(i in from)) {
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+ ar[i] = from[i];
+ }
+ }
+ return to.concat(ar || Array.prototype.slice.call(from));
+};
+exports.__esModule = true;
+exports.useragent = exports.Client = exports.namespace = void 0;
+var mime_types_1 = require("mime-types");
+var axios_1 = require("axios");
+var puppeteer_config_1 = require("../config/puppeteer.config");
+var model_1 = require("./model");
+var errors_1 = require("./model/errors");
+var p_queue_1 = require("p-queue");
+var events_1 = require("../controllers/events");
+var uuid_1 = require("uuid");
+var parse_function_1 = require("parse-function");
+var fs = require("fs");
+var datauri_1 = require("datauri");
+var is_url_superb_1 = require("is-url-superb");
+var fs_extra_1 = require("fs-extra");
+var browser_1 = require("../controllers/browser");
+var auth_1 = require("../controllers/auth");
+var wa_decrypt_1 = require("@open-wa/wa-decrypt");
+var path = require("path");
+var media_1 = require("./model/media");
+var patch_manager_1 = require("../controllers/patch_manager");
+var events_2 = require("./model/events");
+var MessageCollector_1 = require("../structures/MessageCollector");
+var init_patch_1 = require("../controllers/init_patch");
+var preProcessors_1 = require("../structures/preProcessors");
+var tools_1 = require("../utils/tools");
+var logging_1 = require("../logging/logging");
+var pid_utils_1 = require("../utils/pid_utils");
+/** @ignore */
+var pkg = (0, fs_extra_1.readJsonSync)(path.join(__dirname, '../../package.json'));
+var namespace;
+(function (namespace) {
+ namespace["Chat"] = "Chat";
+ namespace["Msg"] = "Msg";
+ namespace["Contact"] = "Contact";
+ namespace["GroupMetadata"] = "GroupMetadata";
+})(namespace = exports.namespace || (exports.namespace = {}));
+/* eslint-enable */
+var Client = /** @class */ (function () {
+ /**
+ * @ignore
+ * @param page [Page] [Puppeteer Page]{@link https://pptr.dev/#?product=Puppeteer&version=v2.1.1&show=api-class-page} running WA Web
+ */
+ function Client(page, createConfig, sessionInfo) {
+ var _this = this;
+ this._currentlyBeingKilled = false;
+ this._refreshing = false;
+ this._loaded = false;
+ this._prio = Number.MAX_SAFE_INTEGER;
+ this._pageListeners = [];
+ this._registeredPageListeners = [];
+ this._onLogoutCallbacks = [];
+ this._queues = {};
+ this._autoEmojiSet = false;
+ this._autoEmojiQ = new p_queue_1["default"]({
+ concurrency: 1,
+ intervalCap: 1,
+ carryoverConcurrencyCount: true
+ });
+ this._onLogoutSet = false;
+ this._preprocIdempotencyCheck = {};
+ /**
+ * This is used to track if a listener is already used via webhook. Before, webhooks used to be set once per listener. Now a listener can be set via multiple webhooks, or revoked from a specific webhook.
+ * For this reason, listeners assigned to a webhook are only set once and map through all possible webhooks to and fire only if the specific listener is assigned.
+ *
+ * Note: This would be much simpler if eventMode was the default (and only) listener strategy.
+ */
+ this._registeredWebhookListeners = {};
+ /**
+ * This exposes a simple express middlware that will allow users to quickly boot up an api based off this client. Checkout demo/index.ts for an example
+ * How to use the middleware:
+ *
+ * ```javascript
+ *
+ * import { create } from '@open-wa/wa-automate';
+ * const express = require('express')
+ * const app = express()
+ * app.use(express.json())
+ * const PORT = 8082;
+ *
+ * function start(client){
+ * app.use(client.middleware()); //or client.middleware(true) if you require the session id to be part of the path (so localhost:8082/sendText beccomes localhost:8082/sessionId/sendText)
+ * app.listen(PORT, function () {
+ * console.log(`\n• Listening on port ${PORT}!`);
+ * });
+ * ...
+ * }
+ *
+ *
+ * create({
+ * sessionId:'session1'
+ * }).then(start)
+ *
+ * ```
+ *
+ * All requests need to be `POST` requests. You use the API the same way you would with `client`. The method can be the path or the method param in the post body. The arguments for the method should be properly ordered in the args array in the JSON post body.
+ *
+ * Example:
+ *
+ * ```javascript
+ * await client.sendText('4477777777777@c.us','test')
+ * //returns "true_4477777777777@c.us_3EB0645E623D91006252"
+ * ```
+ * as a request with a path:
+ *
+ * ```javascript
+ * const axios = require('axios').default;
+ * axios.post('localhost:8082/sendText', {
+ * args: [
+ * "4477777777777@c.us",
+ * "test"
+ * ]
+ * })
+ * ```
+ *
+ * or as a request without a path:
+ *
+ * ```javascript
+ * const axios = require('axios').default;
+ * axios.post('localhost:8082', {
+ * method:'sendText',
+ * args: [
+ * "4477777777777@c.us",
+ * "test"
+ * ]
+ * })
+ * ```
+ *
+ * As of 1.9.69, you can also send the argyments as an object with the keys mirroring the paramater names of the relative client functions
+ *
+ * Example:
+ *
+ * ```javascript
+ * const axios = require('axios').default;
+ * axios.post('localhost:8082', {
+ * method:'sendText',
+ * args: {
+ * "to":"4477777777777@c.us",
+ * "content":"test"
+ * }
+ * })
+ * ```
+ * @param useSessionIdInPath boolean Set this to true if you want to keep each session in it's own path.
+ *
+ * For example, if you have a session with id `host` if you set useSessionIdInPath to true, then all requests will need to be prefixed with the path `host`. E.g `localhost:8082/sendText` becomes `localhost:8082/host/sendText`
+ */
+ this.middleware = function (useSessionIdInPath, PORT) {
+ if (useSessionIdInPath === void 0) { useSessionIdInPath = false; }
+ return function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
+ var methodFromPath, checkProp, checkValue, sessionId, hostAccountNumber, checkPassed, rb, args_1, m, methodRequiresArgs, methodArgs, response, success, error_1, snapshot, snapshotBuffer;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (useSessionIdInPath && !req.path.includes(this._createConfig.sessionId) && this._createConfig.sessionId !== 'session')
+ return [2 /*return*/, next()];
+ methodFromPath = this._createConfig.sessionId && this._createConfig.sessionId !== 'session' && req.path.includes(this._createConfig.sessionId) ? req.path.replace("/".concat(this._createConfig.sessionId, "/"), '') : req.path.replace('/', '');
+ if (!(req.get('owa-check-property') && req.get('owa-check-value'))) return [3 /*break*/, 2];
+ checkProp = req.get('owa-check-property');
+ checkValue = req.get('owa-check-value');
+ sessionId = this._createConfig.sessionId;
+ return [4 /*yield*/, this.getHostNumber()];
+ case 1:
+ hostAccountNumber = _a.sent();
+ checkPassed = false;
+ switch (checkProp) {
+ case 'session':
+ checkPassed = sessionId === checkValue;
+ break;
+ case 'number':
+ checkPassed = hostAccountNumber.includes(checkValue);
+ break;
+ }
+ if (!checkPassed) {
+ if (PORT)
+ (0, tools_1.processSendData)({ port: PORT });
+ return [2 /*return*/, res.status(412).send({
+ success: false,
+ error: {
+ name: 'CHECK_FAILED',
+ message: "Check FAILED - Are you sure you meant to send the request to this session?",
+ data: {
+ incomingCheckProperty: checkProp,
+ incomingCheckValue: checkValue,
+ sessionId: sessionId,
+ hostAccountNumber: "".concat(hostAccountNumber.substr(-4))
+ }
+ }
+ })];
+ }
+ _a.label = 2;
+ case 2:
+ if (!(req.method === 'POST')) return [3 /*break*/, 7];
+ rb = (req === null || req === void 0 ? void 0 : req.body) || {};
+ args_1 = rb.args;
+ m = (rb === null || rb === void 0 ? void 0 : rb.method) || methodFromPath;
+ logging_1.log.info("MDLWR - ".concat(m, " : ").concat(JSON.stringify(rb || {})));
+ methodRequiresArgs = false;
+ if (args_1 && !Array.isArray(args_1)) {
+ methodArgs = (0, parse_function_1["default"])().parse(this[m]).args;
+ logging_1.log.info("methodArgs: ".concat(methodArgs));
+ if ((methodArgs === null || methodArgs === void 0 ? void 0 : methodArgs.length) > 0)
+ methodRequiresArgs = true;
+ args_1 = methodArgs.map(function (argName) { return args_1[argName]; });
+ }
+ else if (!args_1)
+ args_1 = [];
+ if (!this[m]) return [3 /*break*/, 6];
+ _a.label = 3;
+ case 3:
+ _a.trys.push([3, 5, , 6]);
+ return [4 /*yield*/, this[m].apply(this, args_1)];
+ case 4:
+ response = _a.sent();
+ success = true;
+ if (typeof response == 'string' && (response.startsWith("Error") || response.startsWith("ERROR")))
+ success = false;
+ return [2 /*return*/, res.send({
+ success: success,
+ response: response
+ })];
+ case 5:
+ error_1 = _a.sent();
+ console.error("middleware -> error", error_1);
+ if (methodRequiresArgs && Array.isArray(args_1))
+ error_1.message = "".concat((req === null || req === void 0 ? void 0 : req.params) ? "Please set arguments in request json body, not in params." : "Args expected, none found.", " ").concat(error_1.message);
+ return [2 /*return*/, res.send({
+ success: false,
+ error: {
+ name: error_1.name,
+ message: error_1.message,
+ data: error_1.data
+ }
+ })];
+ case 6: return [2 /*return*/, res.status(404).send("Cannot find method: ".concat(m))];
+ case 7:
+ if (!(req.method === "GET")) return [3 /*break*/, 9];
+ if (!["snapshot", "getSnapshot"].includes(methodFromPath)) return [3 /*break*/, 9];
+ return [4 /*yield*/, this.getSnapshot()];
+ case 8:
+ snapshot = _a.sent();
+ snapshotBuffer = Buffer.from(snapshot.split(',')[1], 'base64');
+ res.writeHead(200, {
+ 'Content-Type': 'image/png',
+ 'Content-Length': snapshotBuffer.length
+ });
+ return [2 /*return*/, res.end(snapshotBuffer)];
+ case 9: return [2 /*return*/, next()];
+ }
+ });
+ }); };
+ };
+ this._page = page;
+ this._createConfig = createConfig || {};
+ this._loadedModules = [];
+ this._sessionInfo = sessionInfo;
+ this._sessionInfo.INSTANCE_ID = (0, uuid_1.v4)();
+ this._listeners = {};
+ this._setOnClose();
+ }
+ /**
+ * @private
+ *
+ * DO NOT USE THIS.
+ *
+ * Run all tasks to set up client AFTER init is fully completed
+ */
+ Client.prototype.loaded = function () {
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
+ return __awaiter(this, void 0, void 0, function () {
+ var syncT, _k, ident_1;
+ var _this = this;
+ return __generator(this, function (_l) {
+ switch (_l.label) {
+ case 0:
+ /**
+ * Wait for internal session to load earlier messages
+ */
+ logging_1.log.info('Waiting for internal session to finish syncing');
+ return [4 /*yield*/, (0, tools_1.timePromise)(function () { return _this._page.waitForFunction(function () { return WAPI.isSessionLoaded(); }, { timeout: 20000, polling: 50 }); })["catch"](function () { return 20001; })];
+ case 1:
+ syncT = _l.sent();
+ logging_1.log.info("Internal session finished syncing in ".concat(syncT, "ms"));
+ if (!((_a = this._createConfig) === null || _a === void 0 ? void 0 : _a.eventMode)) return [3 /*break*/, 3];
+ return [4 /*yield*/, this.registerAllSimpleListenersOnEv()];
+ case 2:
+ _l.sent();
+ _l.label = 3;
+ case 3:
+ _k = this._sessionInfo;
+ return [4 /*yield*/, this.getMe()];
+ case 4:
+ _k.PHONE_VERSION = (_c = (_b = (_l.sent())) === null || _b === void 0 ? void 0 : _b.phone) === null || _c === void 0 ? void 0 : _c.wa_version;
+ logging_1.log.info('LOADED', {
+ PHONE_VERSION: this._sessionInfo.PHONE_VERSION
+ });
+ if ((((_d = this._createConfig) === null || _d === void 0 ? void 0 : _d.autoEmoji) === undefined || ((_e = this._createConfig) === null || _e === void 0 ? void 0 : _e.autoEmoji)) && !this._autoEmojiSet) {
+ ident_1 = typeof ((_f = this._createConfig) === null || _f === void 0 ? void 0 : _f.autoEmoji) === "string" ? (_g = this._createConfig) === null || _g === void 0 ? void 0 : _g.autoEmoji : ":";
+ this.onMessage(function (message) { return __awaiter(_this, void 0, void 0, function () {
+ var emojiId_1;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!((message === null || message === void 0 ? void 0 : message.body) && message.body.startsWith(ident_1) && message.body.endsWith(ident_1))) return [3 /*break*/, 2];
+ emojiId_1 = message.body.replace(new RegExp(ident_1, 'g'), "");
+ if (!emojiId_1)
+ return [2 /*return*/];
+ return [4 /*yield*/, this._autoEmojiQ.add(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
+ return [2 /*return*/, this.sendEmoji(message.from, emojiId_1, message.id)["catch"](function () { })];
+ }); }); })];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2: return [2 /*return*/, message];
+ }
+ });
+ }); });
+ this._autoEmojiSet = true;
+ }
+ if ((((_h = this._createConfig) === null || _h === void 0 ? void 0 : _h.deleteSessionDataOnLogout) || ((_j = this._createConfig) === null || _j === void 0 ? void 0 : _j.killClientOnLogout)) && !this._onLogoutSet) {
+ this.onLogout(function () { return __awaiter(_this, void 0, void 0, function () {
+ var _a, _b, _c, _d, _e, _f;
+ return __generator(this, function (_g) {
+ switch (_g.label) {
+ case 0: return [4 /*yield*/, this.waitAllQEmpty()];
+ case 1:
+ _g.sent();
+ return [4 /*yield*/, ((_b = (_a = this._queues) === null || _a === void 0 ? void 0 : _a.onLogout) === null || _b === void 0 ? void 0 : _b.onEmpty())];
+ case 2:
+ _g.sent();
+ return [4 /*yield*/, ((_d = (_c = this._queues) === null || _c === void 0 ? void 0 : _c.onLogout) === null || _d === void 0 ? void 0 : _d.onIdle())];
+ case 3:
+ _g.sent();
+ return [4 /*yield*/, (0, browser_1.invalidateSesssionData)(this._createConfig)];
+ case 4:
+ _g.sent();
+ if (!((_e = this._createConfig) === null || _e === void 0 ? void 0 : _e.deleteSessionDataOnLogout)) return [3 /*break*/, 6];
+ return [4 /*yield*/, (0, browser_1.deleteSessionData)(this._createConfig)];
+ case 5:
+ _g.sent();
+ _g.label = 6;
+ case 6:
+ if ((_f = this._createConfig) === null || _f === void 0 ? void 0 : _f.killClientOnLogout) {
+ console.log("Session logged out. Killing client");
+ logging_1.log.warn("Session logged out. Killing client");
+ this.kill("LOGGED_OUT");
+ }
+ return [2 /*return*/];
+ }
+ });
+ }); }, -1);
+ this._onLogoutSet = true;
+ }
+ this._loaded = true;
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ Client.prototype.registerAllSimpleListenersOnEv = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, Promise.all(Object.keys(events_2.SimpleListener).map(function (eventKey) { return _this.registerEv(events_2.SimpleListener[eventKey]); }))];
+ case 1:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ Client.prototype.getSessionId = function () {
+ return this._createConfig.sessionId || 'session';
+ };
+ Client.prototype.getPage = function () {
+ return this._page;
+ };
+ Client.prototype._setOnClose = function () {
+ var _this = this;
+ this._page.on('close', function () {
+ var _a;
+ if (!_this._refreshing) {
+ console.log("Browser page has closed. Killing client");
+ logging_1.log.warn("Browser page has closed. Killing client");
+ _this.kill("PAGE_CLOSED");
+ if ((_a = _this._createConfig) === null || _a === void 0 ? void 0 : _a.killProcessOnBrowserClose)
+ process.exit();
+ }
+ });
+ };
+ Client.prototype._reInjectWapi = function (newTab) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, (0, browser_1.injectApi)(newTab || this._page, null, true)];
+ case 1:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ Client.prototype._reRegisterListeners = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var _this = this;
+ return __generator(this, function (_a) {
+ return [2 /*return*/, Object.keys(this._listeners).forEach(function (listenerName) { return _this[listenerName](_this._listeners[listenerName]); })];
+ });
+ });
+ };
+ /**
+ * A convinience method to download the [[DataURL]] of a file
+ * @param url The url
+ * @param optionsOverride You can use this to override the [axios request config](https://github.com/axios/axios#request-config)
+ * @returns `Promise`
+ */
+ Client.prototype.download = function (url, optionsOverride) {
+ if (optionsOverride === void 0) { optionsOverride = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, (0, tools_1.getDUrl)(url, optionsOverride)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Grab the logger for this session/process
+ */
+ Client.prototype.logger = function () {
+ return logging_1.log;
+ };
+ /**
+ * Refreshes the page and reinjects all necessary files. This may be useful for when trying to save memory
+ * This will attempt to re register all listeners EXCEPT onLiveLocation and onParticipantChanged
+ */
+ Client.prototype.refresh = function () {
+ var _a, _b, _c, _d;
+ return __awaiter(this, void 0, void 0, function () {
+ var spinner, me, preloadlicense, _e, START_TIME, newTab, qrManager, closePageOnConflict, setupNewPage;
+ var _this = this;
+ return __generator(this, function (_f) {
+ switch (_f.label) {
+ case 0:
+ this._refreshing = true;
+ spinner = new events_1.Spin(((_a = this._createConfig) === null || _a === void 0 ? void 0 : _a.sessionId) || 'session', 'REFRESH', (_b = this._createConfig) === null || _b === void 0 ? void 0 : _b.disableSpins);
+ return [4 /*yield*/, this.getMe()];
+ case 1:
+ me = (_f.sent()).me;
+ if (!((_c = this._createConfig) === null || _c === void 0 ? void 0 : _c.licenseKey)) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, patch_manager_1.getLicense)(this._createConfig, me, this._sessionInfo, spinner)];
+ case 2:
+ _e = _f.sent();
+ return [3 /*break*/, 4];
+ case 3:
+ _e = false;
+ _f.label = 4;
+ case 4:
+ preloadlicense = _e;
+ spinner.info('Refreshing session');
+ START_TIME = Date.now();
+ spinner.info("Opening session in new tab");
+ return [4 /*yield*/, this._page.browser().newPage()];
+ case 5:
+ newTab = _f.sent();
+ qrManager = new auth_1.QRManager(this._createConfig);
+ return [4 /*yield*/, (0, browser_1.initPage)(this.getSessionId(), this._createConfig, qrManager, this._createConfig.customUserAgent, spinner, newTab, true)
+ // await newTab.goto(puppeteerConfig.WAUrl);
+ //Two promises. One that closes the previous page, one that sets up the new page
+ ];
+ case 6:
+ _f.sent();
+ closePageOnConflict = function () { return __awaiter(_this, void 0, void 0, function () {
+ var useHere;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getUseHereString(); })];
+ case 1:
+ useHere = _a.sent();
+ spinner.info("Waiting for conflict to close stale tab...");
+ return [4 /*yield*/, this._page.waitForFunction("[...document.querySelectorAll(\"div[role=button\")].find(e=>{return e.innerHTML.toLowerCase().includes(\"".concat(useHere.toLowerCase(), "\")})"), { timeout: 0, polling: 500 })];
+ case 2:
+ _a.sent();
+ return [4 /*yield*/, this._page.goto('about:blank')];
+ case 3:
+ _a.sent();
+ spinner.info("Closing stale tab");
+ return [4 /*yield*/, this._page.close()];
+ case 4:
+ _a.sent();
+ spinner.info("Stale tab closed. Switching contexts...");
+ this._page = newTab;
+ return [2 /*return*/];
+ }
+ });
+ }); };
+ setupNewPage = function () { return __awaiter(_this, void 0, void 0, function () {
+ var _a, _b;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ /**
+ * Wait for the new page to be loaded up before closing existing page
+ */
+ spinner.info("Checking if fresh session is authenticated...");
+ return [4 /*yield*/, (0, auth_1.isAuthenticated)(newTab)];
+ case 1:
+ if (!_c.sent()) return [3 /*break*/, 10];
+ /**
+ * Reset all listeners
+ */
+ this._registeredEvListeners = {};
+ if (!((_a = this._createConfig) === null || _a === void 0 ? void 0 : _a.waitForRipeSession)) return [3 /*break*/, 4];
+ return [4 /*yield*/, this._reInjectWapi(newTab)];
+ case 2:
+ _c.sent();
+ spinner.start("Waiting for ripe session...");
+ return [4 /*yield*/, (0, auth_1.waitForRipeSession)(newTab)];
+ case 3:
+ if (_c.sent())
+ spinner.succeed("Session ready for injection");
+ else
+ spinner.fail("You may experience issues in headless mode. Continuing...");
+ _c.label = 4;
+ case 4:
+ spinner.info("Injected new session...");
+ return [4 /*yield*/, this._reInjectWapi(newTab)];
+ case 5:
+ _c.sent();
+ /**
+ * patch
+ */
+ return [4 /*yield*/, (0, patch_manager_1.getAndInjectLivePatch)(newTab, spinner, null, this._createConfig, this._sessionInfo)];
+ case 6:
+ /**
+ * patch
+ */
+ _c.sent();
+ if (!((_b = this._createConfig) === null || _b === void 0 ? void 0 : _b.licenseKey)) return [3 /*break*/, 8];
+ return [4 /*yield*/, (0, patch_manager_1.getAndInjectLicense)(newTab, this._createConfig, me, this._sessionInfo, spinner, preloadlicense)];
+ case 7:
+ _c.sent();
+ _c.label = 8;
+ case 8:
+ /**
+ * init patch
+ */
+ return [4 /*yield*/, (0, init_patch_1.injectInitPatch)(newTab)];
+ case 9:
+ /**
+ * init patch
+ */
+ _c.sent();
+ return [3 /*break*/, 11];
+ case 10: throw new Error("Session Logged Out. Cannot refresh. Please restart the process and scan the qr code.");
+ case 11: return [2 /*return*/];
+ }
+ });
+ }); };
+ return [4 /*yield*/, Promise.all([
+ closePageOnConflict(),
+ setupNewPage()
+ ])];
+ case 7:
+ _f.sent();
+ spinner.info("New session live. Setting up...");
+ spinner.info("Reregistering listeners");
+ return [4 /*yield*/, this.loaded()];
+ case 8:
+ _f.sent();
+ if (!!((_d = this._createConfig) === null || _d === void 0 ? void 0 : _d.eventMode)) return [3 /*break*/, 10];
+ return [4 /*yield*/, this._reRegisterListeners()];
+ case 9:
+ _f.sent();
+ _f.label = 10;
+ case 10:
+ spinner.succeed("Session refreshed in ".concat((Date.now() - START_TIME) / 1000, "s"));
+ this._refreshing = false;
+ spinner.remove();
+ this._setOnClose();
+ return [2 /*return*/, true];
+ }
+ });
+ });
+ };
+ /**
+ * Get the session info
+ *
+ * @returns SessionInfo
+ */
+ Client.prototype.getSessionInfo = function () {
+ return this._sessionInfo;
+ };
+ /**
+ * Easily resize page on the fly. Useful if you're showing screenshots in a web-app.
+ */
+ Client.prototype.resizePage = function (width, height) {
+ if (width === void 0) { width = 1920; }
+ if (height === void 0) { height = 1080; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.setViewport({
+ width: width,
+ height: height
+ })];
+ case 1:
+ _a.sent();
+ return [2 /*return*/, true];
+ }
+ });
+ });
+ };
+ /**
+ * Get the config which was used to set up the client. Sensitive details (like devTools username and password, and browserWSEndpoint) are scrubbed
+ *
+ * @returns SessionInfo
+ */
+ Client.prototype.getConfig = function () {
+ /* eslint-disable */
+ var _a = this._createConfig, devtools = _a.devtools, browserWSEndpoint = _a.browserWSEndpoint, sessionData = _a.sessionData, proxyServerCredentials = _a.proxyServerCredentials, restartOnCrash = _a.restartOnCrash, rest = __rest(_a, ["devtools", "browserWSEndpoint", "sessionData", "proxyServerCredentials", "restartOnCrash"]);
+ /* eslint-enable */
+ return rest;
+ };
+ Client.prototype.pup = function (pageFunction) {
+ var _a, _b, _c, _d, _e, _f;
+ var args = [];
+ for (var _i = 1; _i < arguments.length; _i++) {
+ args[_i - 1] = arguments[_i];
+ }
+ return __awaiter(this, void 0, void 0, function () {
+ var invocation_id, _g, safeMode, callTimeout, idCorrection, logging, _t, state, fixId_1, p, wapis, _args, gc_1, mainPromise, res;
+ var _h;
+ var _this = this;
+ return __generator(this, function (_j) {
+ switch (_j.label) {
+ case 0:
+ invocation_id = (0, uuid_1.v4)().slice(-5);
+ _g = this._createConfig, safeMode = _g.safeMode, callTimeout = _g.callTimeout, idCorrection = _g.idCorrection, logging = _g.logging;
+ if (!safeMode) return [3 /*break*/, 2];
+ if (!this._page || this._page.isClosed())
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.PAGE_CLOSED, 'page closed');
+ return [4 /*yield*/, this.forceUpdateConnectionState()];
+ case 1:
+ state = _j.sent();
+ if (state !== model_1.STATE.CONNECTED)
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.STATE_ERROR, "state: ".concat(state));
+ _j.label = 2;
+ case 2:
+ if (idCorrection && args[0]) {
+ fixId_1 = function (id) {
+ var _a;
+ var isGroup = false;
+ var scrubbedId = (_a = id === null || id === void 0 ? void 0 : id.match(/\d|-/g)) === null || _a === void 0 ? void 0 : _a.join('');
+ scrubbedId = scrubbedId.match(/-/g) && scrubbedId.match(/-/g).length == 1 && scrubbedId.split('-')[1].length === 10 ? scrubbedId : scrubbedId.replace(/-/g, '');
+ if (scrubbedId.includes('-') || scrubbedId.length === 18)
+ isGroup = true;
+ var fixed = isGroup ?
+ "".concat(scrubbedId === null || scrubbedId === void 0 ? void 0 : scrubbedId.replace(/@(c|g).us/g, ''), "@g.us") :
+ "".concat(scrubbedId === null || scrubbedId === void 0 ? void 0 : scrubbedId.replace(/@(c|g).us/g, ''), "@c.us");
+ logging_1.log.info('Fixed ID', { id: id, fixed: fixed });
+ return fixed;
+ };
+ if (typeof args[0] === 'string' && args[0] && !(args[0].includes("@g.us") || args[0].includes("@c.us")) && ((_c = (_b = (((_a = pageFunction === null || pageFunction === void 0 ? void 0 : pageFunction.toString()) === null || _a === void 0 ? void 0 : _a.match(/[^(]*\(([^)]*)\)/)[1]) || "")) === null || _b === void 0 ? void 0 : _b.replace(/\s/g, '')) === null || _c === void 0 ? void 0 : _c.split(','))) {
+ p = ((pageFunction === null || pageFunction === void 0 ? void 0 : pageFunction.toString().match(/[^(]*\(([^)]*)\)/)[1]) || "").replace(/\s/g, '').split(',');
+ if (["to", "chatId", "groupChatId", "groupId", "contactId"].includes(p[0]))
+ args[0] = fixId_1(args[0]);
+ }
+ else if (typeof args[0] === 'object')
+ Object.entries(args[0]).map(function (_a) {
+ var k = _a[0], v = _a[1];
+ if (["to", "chatId", "groupChatId", "groupId", "contactId"].includes(k) && typeof v == "string" && v && !(v.includes("@g.us") || v.includes("@c.us"))) {
+ args[0][k] = fixId_1(v);
+ }
+ });
+ }
+ if (logging) {
+ wapis = (_e = (((_d = pageFunction === null || pageFunction === void 0 ? void 0 : pageFunction.toString()) === null || _d === void 0 ? void 0 : _d.match(/WAPI\.(\w*)\(/g)) || [])) === null || _e === void 0 ? void 0 : _e.map(function (s) { return s.replace(/WAPI|\.|\(/g, ''); });
+ _t = Date.now();
+ _args = ["string", "number", "boolean"].includes(typeof args[0]) ? args[0] : __assign({}, args[0]);
+ logging_1.log.info("IN ".concat(invocation_id), {
+ _method: (wapis === null || wapis === void 0 ? void 0 : wapis.length) === 1 ? wapis[0] : wapis,
+ _args: _args
+ });
+ }
+ if (!((_f = this._createConfig) === null || _f === void 0 ? void 0 : _f.aggressiveGarbageCollection)) return [3 /*break*/, 4];
+ return [4 /*yield*/, this._page.evaluate(function () { return gc_1(); })];
+ case 3:
+ gc_1 = _j.sent();
+ _j.label = 4;
+ case 4:
+ mainPromise = (_h = this._page).evaluate.apply(_h, __spreadArray([pageFunction], args, false));
+ if (!callTimeout) return [3 /*break*/, 6];
+ return [4 /*yield*/, Promise.race([mainPromise, new Promise(function (resolve, reject) { var _a; return setTimeout(reject, (_a = _this._createConfig) === null || _a === void 0 ? void 0 : _a.callTimeout, new errors_1.PageEvaluationTimeout()); })])];
+ case 5: return [2 /*return*/, _j.sent()];
+ case 6: return [4 /*yield*/, mainPromise];
+ case 7:
+ res = _j.sent();
+ if (_t && logging) {
+ logging_1.log.info("OUT ".concat(invocation_id, ": ").concat(Date.now() - _t, "ms"), { res: res });
+ }
+ return [2 /*return*/, this.responseWrap(res)];
+ }
+ });
+ });
+ };
+ Client.prototype.responseWrap = function (res) {
+ if (this._loaded && typeof res === "string" && res.includes("requires") && res.includes("license")) {
+ console.info('\x1b[36m', "🔶", res, "🔶", '\x1b[0m');
+ }
+ if (this._createConfig.onError && typeof res == "string" && (res.startsWith("Error") || res.startsWith("ERROR"))) {
+ var e = this._createConfig.onError;
+ /**
+ * Log error
+ */
+ if (e == model_1.OnError.LOG_AND_FALSE ||
+ e == model_1.OnError.LOG_AND_STRING ||
+ res.includes("get.openwa.dev"))
+ console.error(res);
+ /**
+ * Return res
+ */
+ if (e == model_1.OnError.AS_STRING ||
+ e == model_1.OnError.NOTHING ||
+ e == model_1.OnError.LOG_AND_STRING)
+ return res;
+ /**
+ * Return false
+ */
+ if (e == model_1.OnError.LOG_AND_FALSE ||
+ e == model_1.OnError.RETURN_FALSE)
+ return false;
+ if (e == model_1.OnError.RETURN_ERROR)
+ return new Error(res);
+ if (e == model_1.OnError.THROW)
+ throw new Error(res);
+ }
+ return res;
+ };
+ /**
+ * //////////////////////// LISTENERS
+ */
+ Client.prototype.removeListener = function (listener) {
+ events_1.ev.removeAllListeners(this.getEventSignature(listener));
+ return true;
+ };
+ Client.prototype.removeAllListeners = function () {
+ var _this = this;
+ Object.keys(this._registeredEvListeners).map(function (listener) { return events_1.ev.removeAllListeners(_this.getEventSignature(listener)); });
+ return true;
+ };
+ /**
+ *
+ */
+ Client.prototype.registerListener = function (funcName, _fn, queueOptions) {
+ return __awaiter(this, void 0, void 0, function () {
+ var fn, set, exists, res;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (queueOptions) {
+ if (!this._queues[funcName]) {
+ this._queues[funcName] = new p_queue_1["default"](queueOptions);
+ }
+ fn = function (data) { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this._queues[funcName].add(function () { return _fn(data); }, {
+ priority: this.tickPriority()
+ })];
+ });
+ }); };
+ }
+ else {
+ fn = _fn;
+ }
+ if (this._registeredEvListeners && this._registeredEvListeners[funcName]) {
+ return [2 /*return*/, events_1.ev.on(this.getEventSignature(funcName), function (_a) {
+ var data = _a.data;
+ return fn(data);
+ }, { objectify: true })];
+ }
+ set = function () { return _this.pup(function (_a) {
+ var funcName = _a.funcName;
+ //@ts-ignore
+ return window[funcName] ? WAPI["".concat(funcName)](function (obj) { return window[funcName](obj); }) : false;
+ }, { funcName: funcName }); };
+ if (this._listeners[funcName] && !this._refreshing) {
+ return [2 /*return*/, true];
+ }
+ this._listeners[funcName] = fn;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var checkFuncName = _a.checkFuncName;
+ return window[checkFuncName] ? true : false;
+ }, { checkFuncName: funcName })];
+ case 1:
+ exists = _a.sent();
+ if (!exists) return [3 /*break*/, 3];
+ return [4 /*yield*/, set()];
+ case 2: return [2 /*return*/, _a.sent()];
+ case 3: return [4 /*yield*/, this._page.exposeFunction(funcName, function (obj) { return fn(obj); }).then(set)["catch"](function () { return set; })];
+ case 4:
+ res = _a.sent();
+ return [2 /*return*/, res];
+ }
+ });
+ });
+ };
+ // NON-STANDARD LISTENERS
+ Client.prototype.registerPageEventListener = function (_event, callback, priority) {
+ var _this = this;
+ var event = _event;
+ this._pageListeners.push({
+ event: event,
+ callback: callback,
+ priority: priority
+ });
+ if (this._registeredPageListeners.includes(event))
+ return true;
+ this._registeredPageListeners.push(event);
+ logging_1.log.info("setting page listener: ".concat(String(event)), this._registeredPageListeners);
+ this._page.on(event, function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, Promise.all(this._pageListeners.filter(function (l) { return l.event === event; }).filter(function (_a) {
+ var priority = _a.priority;
+ return priority !== -1;
+ }).sort(function (a, b) { return (b.priority || 0) - (a.priority || 0); }).map(function (l) { return l.callback.apply(l, args); }))];
+ case 1:
+ _a.sent();
+ return [4 /*yield*/, Promise.all(this._pageListeners.filter(function (l) { return l.event === event; }).filter(function (_a) {
+ var priority = _a.priority;
+ return priority == -1;
+ }).sort(function (a, b) { return (b.priority || 0) - (a.priority || 0); }).map(function (l) { return l.callback.apply(l, args); }))];
+ case 2:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+ });
+ };
+ /**
+ * It calls the JavaScript garbage collector
+ * @returns Nothing.
+ */
+ Client.prototype.gc = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return gc(); })];
+ case 1:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /**
+ * Listens to a log out event
+ *
+ * @event
+ * @param fn callback
+ * @param priority A priority of -1 will mean the callback will be triggered after all the non -1 callbacks
+ * @fires `true`
+ */
+ Client.prototype.onLogout = function (fn, priority) {
+ return __awaiter(this, void 0, void 0, function () {
+ var event;
+ var _this = this;
+ return __generator(this, function (_a) {
+ event = 'framenavigated';
+ this._onLogoutCallbacks.push({
+ callback: fn,
+ priority: priority
+ });
+ if (!this._queues[event])
+ this._queues[event] = new p_queue_1["default"]({
+ concurrency: 1,
+ intervalCap: 1,
+ carryoverConcurrencyCount: true
+ });
+ if (this._registeredPageListeners.includes(event))
+ return [2 /*return*/, true];
+ this.registerPageEventListener(event, function (frame) { return __awaiter(_this, void 0, void 0, function () {
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!frame.url().includes('post_logout=1')) return [3 /*break*/, 5];
+ console.log("LOGGED OUT");
+ logging_1.log.warn("LOGGED OUT");
+ return [4 /*yield*/, Promise.all(this._onLogoutCallbacks.filter(function (c) { return c.priority !== -1; }).map(function (_a) {
+ var callback = _a.callback;
+ return _this._queues[event].add(function () { return callback(true); });
+ }))];
+ case 1:
+ _a.sent();
+ return [4 /*yield*/, this._queues[event].onEmpty()];
+ case 2:
+ _a.sent();
+ return [4 /*yield*/, Promise.all(this._onLogoutCallbacks.filter(function (c) { return c.priority == -1; }).map(function (_a) {
+ var callback = _a.callback;
+ return _this._queues[event].add(function () { return callback(true); });
+ }))];
+ case 3:
+ _a.sent();
+ return [4 /*yield*/, this._queues[event].onEmpty()];
+ case 4:
+ _a.sent();
+ _a.label = 5;
+ case 5: return [2 /*return*/];
+ }
+ });
+ }); }, priority || 1);
+ return [2 /*return*/, true];
+ });
+ });
+ };
+ /**
+ * Wait for the webhook queue to become idle. This is useful for ensuring webhooks are cleared before ending a process.
+ */
+ Client.prototype.waitWhQIdle = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!this._webhookQueue) return [3 /*break*/, 2];
+ return [4 /*yield*/, this._webhookQueue.onIdle()];
+ case 1: return [2 /*return*/, _a.sent()];
+ case 2: return [2 /*return*/, true];
+ }
+ });
+ });
+ };
+ /**
+ * Wait for all queues to be empty
+ */
+ Client.prototype.waitAllQEmpty = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, Promise.all(__spreadArray([
+ this._webhookQueue
+ ], Object.values(this._queues), true).filter(function (q) { return q; }).map(function (q) { return q === null || q === void 0 ? void 0 : q.onEmpty(); }))];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * If you have set `onAnyMessage` or `onMessage` with the second parameter (PQueue options) then you may want to inspect their respective PQueue's.
+ */
+ Client.prototype.getListenerQueues = function () {
+ return this._queues;
+ };
+ // STANDARD SIMPLE LISTENERS
+ Client.prototype.preprocessMessage = function (message, source) {
+ return __awaiter(this, void 0, void 0, function () {
+ var alreadyProcessed, fil, m, _m_1, preprocres;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ alreadyProcessed = false;
+ if (this._preprocIdempotencyCheck[message.id]) {
+ logging_1.log.info("preprocessMessage: ".concat(message.id, " already being processed"));
+ // return message;
+ alreadyProcessed = true;
+ }
+ this._preprocIdempotencyCheck[message.id] = true;
+ fil = "";
+ try {
+ fil = typeof this._createConfig.preprocFilter == "function" ? this._createConfig.preprocFilter : typeof this._createConfig.preprocFilter == "string" ? eval(this._createConfig.preprocFilter || "undefined") : undefined;
+ }
+ catch (error) {
+ //do nothing
+ }
+ m = fil ? [message].filter(typeof fil == "function" ? fil : function (x) { return x; })[0] : message;
+ if (!(m && this._createConfig.messagePreprocessor)) return [3 /*break*/, 2];
+ if (!Array.isArray(this._createConfig.messagePreprocessor))
+ this._createConfig.messagePreprocessor = [this._createConfig.messagePreprocessor];
+ _m_1 = m;
+ return [4 /*yield*/, Promise.all(this._createConfig.messagePreprocessor.map(function (preproc, index) { return __awaiter(_this, void 0, void 0, function () {
+ var custom, start;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ custom = false;
+ start = Date.now();
+ if (!(typeof preproc === "function")) return [3 /*break*/, 2];
+ custom = true;
+ return [4 /*yield*/, preproc(_m_1, this, alreadyProcessed, source)];
+ case 1:
+ _m_1 = _a.sent();
+ return [3 /*break*/, 4];
+ case 2:
+ if (!(typeof preproc === "string" && preProcessors_1.MessagePreprocessors[preproc])) return [3 /*break*/, 4];
+ return [4 /*yield*/, preProcessors_1.MessagePreprocessors[preproc](_m_1, this, alreadyProcessed, source)];
+ case 3:
+ _m_1 = _a.sent();
+ _a.label = 4;
+ case 4:
+ logging_1.log.info("Preproc ".concat(custom ? 'CUSTOM' : preproc, " ").concat(index, " ").concat(fil, " ").concat(message.id, " ").concat(m.id, " ").concat(Date.now() - start, "ms"));
+ return [2 /*return*/, _m_1];
+ }
+ });
+ }); }))];
+ case 1:
+ _a.sent();
+ preprocres = _m_1 || message;
+ delete this._preprocIdempotencyCheck[message.id];
+ return [2 /*return*/, preprocres];
+ case 2:
+ delete this._preprocIdempotencyCheck[message.id];
+ return [2 /*return*/, message];
+ }
+ });
+ });
+ };
+ /**
+ * Listens to incoming messages
+ *
+ * @event
+ * @param fn callback
+ * @param queueOptions PQueue options. Set to `{}` for default PQueue.
+ * @fires [[Message]]
+ */
+ Client.prototype.onMessage = function (fn, queueOptions) {
+ var _a;
+ return __awaiter(this, void 0, void 0, function () {
+ var _fn;
+ var _this = this;
+ return __generator(this, function (_b) {
+ _fn = function (message) { return __awaiter(_this, void 0, void 0, function () { var _a; return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ _a = fn;
+ return [4 /*yield*/, this.preprocessMessage(message, 'onMessage')];
+ case 1: return [2 /*return*/, _a.apply(void 0, [_b.sent()])];
+ }
+ }); }); };
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Message, _fn, ((_a = this === null || this === void 0 ? void 0 : this._createConfig) === null || _a === void 0 ? void 0 : _a.pQueueDefault) || queueOptions)];
+ });
+ });
+ };
+ /**
+ * Listens to all new messages
+ *
+ * @event
+ * @param fn callback
+ * @param queueOptions PQueue options. Set to `{}` for default PQueue.
+ * @fires [[Message]]
+ */
+ Client.prototype.onAnyMessage = function (fn, queueOptions) {
+ var _a;
+ return __awaiter(this, void 0, void 0, function () {
+ var _fn;
+ var _this = this;
+ return __generator(this, function (_b) {
+ _fn = function (message) { return __awaiter(_this, void 0, void 0, function () { var _a; return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ _a = fn;
+ return [4 /*yield*/, this.preprocessMessage(message, 'onAnyMessage')];
+ case 1: return [2 /*return*/, _a.apply(void 0, [_b.sent()])];
+ }
+ }); }); };
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.AnyMessage, _fn, ((_a = this === null || this === void 0 ? void 0 : this._createConfig) === null || _a === void 0 ? void 0 : _a.pQueueDefault) || queueOptions)];
+ });
+ });
+ };
+ /**
+ *
+ * Listens to when a message is deleted by a recipient or the host account
+ * @event
+ * @param fn callback
+ * @fires [[Message]]
+ */
+ Client.prototype.onMessageDeleted = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.MessageDeleted, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to when a chat is deleted by the host account
+ * @event
+ * @param fn callback
+ * @fires [[Chat]]
+ */
+ Client.prototype.onChatDeleted = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.ChatDeleted, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to button message responses
+ * @event
+ * @param fn callback
+ * @fires [[Message]]
+ */
+ Client.prototype.onButton = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Button, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to poll vote events
+ * @event
+ * @param fn callback
+ * @fires [[PollData]]
+ */
+ Client.prototype.onPollVote = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.PollVote, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to broadcast messages
+ * @event
+ * @param fn callback
+ * @fires [[Message]]
+ */
+ Client.prototype.onBroadcast = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Broadcast, fn)];
+ });
+ });
+ };
+ /**
+ * @deprecated
+ *
+ * Listens to battery changes
+ *
+ * :::caution
+ *
+ * This will most likely not work with multi-device mode (the only remaining mode) since the session is no longer connected to the phone but directly to WA servers.
+ *
+ * :::
+ *
+ * @event
+ * @param fn callback
+ * @fires number
+ */
+ Client.prototype.onBattery = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Battery, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to when host device is plugged/unplugged
+ * @event
+ *
+ * @param fn callback
+ * @fires boolean true if plugged, false if unplugged
+ */
+ Client.prototype.onPlugged = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Plugged, fn)];
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Listens to when a contact posts a new story.
+ * @event
+ *
+ * @param fn callback
+ * @fires e.g
+ *
+ * ```javascript
+ * {
+ * from: '123456789@c.us'
+ * id: 'false_132234234234234@status.broadcast'
+ * }
+ * ```
+ */
+ Client.prototype.onStory = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Story, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to changes in state
+ *
+ * @event
+ * @fires STATE observable sream of states
+ */
+ Client.prototype.onStateChanged = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.StateChanged, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to new incoming calls
+ * @event
+ * @returns Observable stream of call request objects
+ */
+ Client.prototype.onIncomingCall = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.IncomingCall, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to changes on call state
+ * @event
+ * @returns Observable stream of call objects
+ */
+ Client.prototype.onCallState = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.CallState, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to label change events
+ *
+ * @event
+ * @param fn callback
+ * @fires [[Label]]
+ */
+ Client.prototype.onLabel = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Label, fn)];
+ });
+ });
+ };
+ /**
+ *{@license:insiders@}
+ *
+ * Listens to new orders. Only works on business accounts
+ */
+ Client.prototype.onOrder = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Order, fn)];
+ });
+ });
+ };
+ /**
+ *{@license:insiders@}
+ *
+ * Listens to new orders. Only works on business accounts
+ */
+ Client.prototype.onNewProduct = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.NewProduct, fn)];
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Listens to reaction add and change events
+ *
+ * @event
+ * @param fn callback
+ * @fires [[ReactionEvent]]
+ */
+ Client.prototype.onReaction = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Reaction, fn)];
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Listens to chat state, including when a specific user is recording and typing within a group chat.
+ *
+ * @event
+ *
+ * Here is an example of the fired object:
+ *
+ * @fires
+ * ```javascript
+ * {
+ * "chat": "00000000000-1111111111@g.us", //the chat in which this state is occuring
+ * "user": "22222222222@c.us", //the user that is causing this state
+ * "state": "composing, //can also be 'available', 'unavailable', 'recording' or 'composing'
+ * }
+ * ```
+ */
+ Client.prototype.onChatState = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.ChatState, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to messages acknowledgement Changes
+ *
+ * @param fn callback function that handles a [[Message]] as the first and only parameter.
+ * @event
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onAck = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ var _fn;
+ var _this = this;
+ return __generator(this, function (_a) {
+ _fn = function (message) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
+ return [2 /*return*/, fn(message)];
+ }); }); };
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.Ack, _fn)];
+ });
+ });
+ };
+ /**
+ * Listens to add and remove events on Groups on a global level. It is memory efficient and doesn't require a specific group id to listen to.
+ *
+ * @event
+ * @param fn callback function that handles a [[ParticipantChangedEventModel]] as the first and only parameter.
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onGlobalParticipantsChanged = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.GlobalParticipantsChanged, fn)];
+ });
+ });
+ };
+ /**
+ * Listents to group approval requests. Emits a message object. Use it with `message.isGroupApprovalRequest()` to check if it is a group approval request.
+ *
+ * @event
+ * @param fn callback function that handles a [[Message]] as the first and only parameter.
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onGroupApprovalRequest = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.GroupApprovalRequest, fn)];
+ });
+ });
+ };
+ /**
+ * Listens to all group (gp2) events. This can be useful if you want to catch when a group title, subject or picture is changed.
+ *
+ * @event
+ * @param fn callback function that handles a [[ParticipantChangedEventModel]] as the first and only parameter.
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onGroupChange = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.GroupChange, fn)];
+ });
+ });
+ };
+ /**
+ * Fires callback with Chat object every time the host phone is added to a group.
+ *
+ * @event
+ * @param fn callback function that handles a [[Chat]] (group chat) as the first and only parameter.
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onAddedToGroup = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.AddedToGroup, fn)];
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Fires callback with Chat object every time the host phone is removed to a group.
+ *
+ * @event
+ * @param fn callback function that handles a [[Chat]] (group chat) as the first and only parameter.
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onRemovedFromGroup = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.RemovedFromGroup, fn)];
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Fires callback with the relevant chat id every time the user clicks on a chat. This will only work in headful mode.
+ *
+ * @event
+ * @param fn callback function that handles a [[ChatId]] as the first and only parameter.
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onChatOpened = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.ChatOpened, fn)];
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Fires callback with contact id when a new contact is added on the host phone.
+ *
+ * @event
+ * @param fn callback function that handles a [[Chat]] as the first and only parameter.
+ * @returns `true` if the callback was registered
+ */
+ Client.prototype.onContactAdded = function (fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.registerListener(events_2.SimpleListener.ChatOpened, fn)];
+ });
+ });
+ };
+ // COMPLEX LISTENERS
+ /**
+ * @event
+ * Listens to add and remove events on Groups. This can no longer determine who commited the action and only reports the following events add, remove, promote, demote
+ * @param groupId group id: xxxxx-yyyy@c.us
+ * @param fn callback
+ * @returns Observable stream of participantChangedEvent
+ */
+ Client.prototype.onParticipantsChanged = function (groupId, fn, legacy) {
+ if (legacy === void 0) { legacy = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ var funcName;
+ var _this = this;
+ return __generator(this, function (_a) {
+ funcName = "onParticipantsChanged_" + groupId.replace('_', "").replace('_', "");
+ return [2 /*return*/, this._page.exposeFunction(funcName, function (participantChangedEvent) {
+ return fn(participantChangedEvent);
+ })
+ .then(function () { return _this.pup(function (_a) {
+ var groupId = _a.groupId, funcName = _a.funcName, legacy = _a.legacy;
+ //@ts-ignore
+ if (legacy)
+ return WAPI._onParticipantsChanged(groupId, window[funcName]);
+ else
+ return WAPI.onParticipantsChanged(groupId, window[funcName]);
+ }, { groupId: groupId, funcName: funcName, legacy: legacy }); })];
+ });
+ });
+ };
+ /**
+ * @event Listens to live locations from a chat that already has valid live locations
+ * @param chatId the chat from which you want to subscribes to live location updates
+ * @param fn callback that takes in a LiveLocationChangedEvent
+ * @returns boolean, if returns false then there were no valid live locations in the chat of chatId
+ * @emits `` LiveLocationChangedEvent
+ */
+ Client.prototype.onLiveLocation = function (chatId, fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ var funcName;
+ var _this = this;
+ return __generator(this, function (_a) {
+ funcName = "onLiveLocation_" + chatId.replace('_', "").replace('_', "");
+ return [2 /*return*/, this._page.exposeFunction(funcName, function (liveLocationChangedEvent) {
+ return fn(liveLocationChangedEvent);
+ })
+ .then(function () { return _this.pup(function (_a) {
+ var chatId = _a.chatId, funcName = _a.funcName;
+ //@ts-ignore
+ return WAPI.onLiveLocation(chatId, window[funcName]);
+ }, { chatId: chatId, funcName: funcName }); })];
+ });
+ });
+ };
+ /**
+ * Use this simple command to test firing callback events.
+ *
+ * @param callbackToTest
+ * @param testData
+ * @returns `false` if the callback was not registered/does not exist
+ */
+ Client.prototype.testCallback = function (callbackToTest, testData) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.pup(function (_a) {
+ var callbackToTest = _a.callbackToTest, testData = _a.testData;
+ return WAPI.testCallback(callbackToTest, testData);
+ }, { callbackToTest: callbackToTest, testData: testData })];
+ });
+ });
+ };
+ /**
+ * Set presence to available or unavailable.
+ * @param available if true it will set your presence to 'online', false will set to unavailable (i.e no 'online' on recipients' phone);
+ */
+ Client.prototype.setPresence = function (available) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (available) { return WAPI.setPresence(available); }, available)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * set your about me
+ * @param newStatus String new profile status
+ */
+ Client.prototype.setMyStatus = function (newStatus) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var newStatus = _a.newStatus;
+ return WAPI.setMyStatus(newStatus);
+ }, { newStatus: newStatus })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Adds label from chat, message or contact. Only for business accounts.
+ * @param label: The desired text of the new label. id will be something simple like anhy nnumber from 1-10, name is the label of the label if that makes sense.
+ * @returns `false` if something went wrong, or the id (usually a number as a string) of the new label (for example `"58"`)
+ */
+ Client.prototype.createLabel = function (label) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var label = _a.label;
+ return WAPI.createLabel(label);
+ }, { label: label })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Adds label from chat, message or contact. Only for business accounts.
+ * @param label: either the id or the name of the label. id will be something simple like anhy nnumber from 1-10, name is the label of the label if that makes sense.
+ * @param id The Chat, message or contact id to which you want to add a label
+ */
+ Client.prototype.addLabel = function (label, chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var label = _a.label, chatId = _a.chatId;
+ return WAPI.addOrRemoveLabels(label, chatId, 'add');
+ }, { label: label, chatId: chatId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns all labels and the corresponding tagged items.
+ */
+ Client.prototype.getAllLabels = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getAllLabels(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Removes label from chat, message or contact. Only for business accounts.
+ * @param label: either the id or the name of the label. id will be something simple like anhy nnumber from 1-10, name is the label of the label if that makes sense.
+ * @param id The Chat, message or contact id to which you want to add a label
+ */
+ Client.prototype.removeLabel = function (label, chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var label = _a.label, chatId = _a.chatId;
+ return WAPI.addOrRemoveLabels(label, chatId, 'remove');
+ }, { label: label, chatId: chatId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get an array of chats that match the label parameter. For example, if you want to get an array of chat objects that have the label "New customer".
+ *
+ * This method is case insenstive and only works on business host accounts.
+ *
+ * @label The label name
+ */
+ Client.prototype.getChatsByLabel = function (label) {
+ return __awaiter(this, void 0, void 0, function () {
+ var res;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var label = _a.label;
+ return WAPI.getChatsByLabel(label);
+ }, { label: label })];
+ case 1:
+ res = _a.sent();
+ if (typeof res == 'string')
+ new errors_1.CustomError(errors_1.ERROR_NAME.INVALID_LABEL, res);
+ return [2 /*return*/, res];
+ }
+ });
+ });
+ };
+ /**
+ * Send VCARD
+ *
+ * @param {string} chatId '000000000000@c.us'
+ * @param {string} vcard vcard as a string, you can send multiple contacts vcard also.
+ * @param {string} contactName The display name for the contact. Ignored on multiple vcards
+ * @param {string} contactNumber If supplied, this will be injected into the vcard (VERSION 3 ONLY FROM VCARDJS) with the WA id to make it show up with the correct buttons on WA. The format of this param should be including country code, without any other formating. e.g:
+ * `4477777777777`
+ * Ignored on multiple vcards
+ */
+ Client.prototype.sendVCard = function (chatId, vcard, contactName, contactNumber) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, vcard = _a.vcard, contactName = _a.contactName, contactNumber = _a.contactNumber;
+ return WAPI.sendVCard(chatId, vcard, contactName, contactNumber);
+ }, { chatId: chatId, vcard: vcard, contactName: contactName, contactNumber: contactNumber })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Set your profile name
+ *
+ * Please note, this does not work on business accounts!
+ *
+ * @param newName String new name to set for your profile
+ */
+ Client.prototype.setMyName = function (newName) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var newName = _a.newName;
+ return WAPI.setMyName(newName);
+ }, { newName: newName })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sets the chat state
+ * @param {ChatState|0|1|2} chatState The state you want to set for the chat. Can be TYPING (0), RECRDING (1) or PAUSED (2).
+ * @param {String} chatId
+ */
+ Client.prototype.setChatState = function (chatState, chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatState = _a.chatState, chatId = _a.chatId;
+ return WAPI.setChatState(chatState, chatId);
+ },
+ //@ts-ignore
+ { chatState: chatState, chatId: chatId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns the connection state
+ */
+ Client.prototype.getConnectionState = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getState(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retreive an array of messages that are not yet sent to the recipient via the host account device (i.e no ticks)
+ */
+ Client.prototype.getUnsentMessages = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getUnsentMessages(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Forces the session to update the connection state.
+ * @param killBeforeAttemptingToReconnect Setting this to true will force the session to drop the current socket connection before attempting to reconnect. This is useful if you want to force the session to reconnect immediately.
+ * @returns updated connection state
+ */
+ Client.prototype.forceUpdateConnectionState = function (killBeforeReconnect) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function (killBeforeReconnect) { return WAPI.forceUpdateConnectionState(killBeforeReconnect); }, killBeforeReconnect)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns a list of contact with whom the host number has an existing chat who are also not contacts.
+ */
+ Client.prototype.getChatWithNonContacts = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getChatWithNonContacts(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Shuts down the page and browser
+ * @returns true
+ */
+ Client.prototype.kill = function (reason) {
+ var _a, _b;
+ if (reason === void 0) { reason = "MANUALLY_KILLED"; }
+ return __awaiter(this, void 0, void 0, function () {
+ var browser, pid, error_2;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ if (this._currentlyBeingKilled)
+ return [2 /*return*/];
+ this._currentlyBeingKilled = true;
+ console.log("Killing client. Shutting Down: ".concat(reason));
+ logging_1.log.info("Killing client. Shutting Down: ".concat(reason));
+ return [4 /*yield*/, ((_a = this === null || this === void 0 ? void 0 : this._page) === null || _a === void 0 ? void 0 : _a.browser())];
+ case 1:
+ browser = _c.sent();
+ pid = (browser === null || browser === void 0 ? void 0 : browser.process()) ? (_b = browser === null || browser === void 0 ? void 0 : browser.process()) === null || _b === void 0 ? void 0 : _b.pid : null;
+ _c.label = 2;
+ case 2:
+ _c.trys.push([2, 4, , 5]);
+ return [4 /*yield*/, (0, browser_1.kill)(this._page, browser, false, pid, reason)];
+ case 3:
+ _c.sent();
+ return [3 /*break*/, 5];
+ case 4:
+ error_2 = _c.sent();
+ return [3 /*break*/, 5];
+ case 5:
+ this._currentlyBeingKilled = false;
+ return [2 /*return*/, true];
+ }
+ });
+ });
+ };
+ /**
+ * This is a convinient method to click the `Use Here` button in the WA web session.
+ *
+ * Use this when [[STATE]] is `CONFLICT`. You can read more about managing state here:
+ *
+ * [[Detecting Logouts]]
+ */
+ Client.prototype.forceRefocus = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var useHere;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getUseHereString(); })];
+ case 1:
+ useHere = _a.sent();
+ return [4 /*yield*/, this._page.waitForFunction("[...document.querySelectorAll(\"div[role=button\")].find(e=>{return e.innerHTML.toLowerCase().includes(\"".concat(useHere.toLowerCase(), "\")})"), { timeout: 0 })];
+ case 2:
+ _a.sent();
+ return [4 /*yield*/, this._page.evaluate("[...document.querySelectorAll(\"div[role=button\")].find(e=>{return e.innerHTML.toLowerCase().includes(\"".concat(useHere.toLowerCase(), "\")}).click()"))];
+ case 3:
+ _a.sent();
+ return [2 /*return*/, true];
+ }
+ });
+ });
+ };
+ /**
+ * Check if the "Phone not Cconnected" message is showing in the browser. If it is showing, then this will return `true`.
+ *
+ * @returns `boolean`
+ */
+ Client.prototype.isPhoneDisconnected = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var phoneNotConnected;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getLocaledString('active Internet connection'); })];
+ case 1:
+ phoneNotConnected = _a.sent();
+ return [4 /*yield*/, this.pup("!![...document.querySelectorAll(\"div\")].find(e=>{return e.innerHTML.toLowerCase().includes(\"".concat(phoneNotConnected.toLowerCase(), "\")})"))];
+ case 2:
+ //@ts-ignore
+ return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Runs a health check to help you determine if/when is an appropiate time to restart/refresh the session.
+ */
+ Client.prototype.healthCheck = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.healthCheck(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get the stats of the current process and the corresponding browser process.
+ */
+ Client.prototype.getProcessStats = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, (0, pid_utils_1.pidTreeUsage)([process.pid, this._page.browser().process().pid])];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ Client.prototype.getIp = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return __awaiter(_this, void 0, void 0, function () {
+ var response, data, error_3;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _a.trys.push([0, 3, , 4]);
+ return [4 /*yield*/, fetch('https://api.ipify.org?format=json')];
+ case 1:
+ response = _a.sent();
+ return [4 /*yield*/, response.json()];
+ case 2:
+ data = _a.sent();
+ return [2 /*return*/, data.ip];
+ case 3:
+ error_3 = _a.sent();
+ return [2 /*return*/, error_3.message];
+ case 4: return [2 /*return*/];
+ }
+ });
+ }); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * A list of participants in the chat who have their live location on. If the chat does not exist, or the chat does not have any contacts actively sharing their live locations, it will return false. If it's a chat with a single contact, there will be only 1 value in the array if the contact has their livelocation on.
+ * Please note. This should only be called once every 30 or so seconds. This forces the phone to grab the latest live location data for the number. This can be used in conjunction with onLiveLocation (this will trigger onLiveLocation).
+ * @param chatId string Id of the chat you want to force the phone to get the livelocation data for.
+ * @returns `Promise` | boolean
+ */
+ Client.prototype.forceUpdateLiveLocation = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId;
+ return WAPI.forceUpdateLiveLocation(chatId);
+ }, { chatId: chatId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * @deprecated
+ *
+ * :::danger
+ *
+ * Buttons are broken for the foreseeable future. Please DO NOT get a license solely for access to buttons. They are no longer reliable due to recent changes at WA.
+ *
+ * :::
+ *
+ * Test the button commands on MD accounts with an insiders key. This is a temporary feature to help fix issue #2658
+ */
+ Client.prototype.testButtons = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId;
+ return WAPI.testButtons(chatId);
+ }, { chatId: chatId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ Client.prototype.link = function (params) {
+ var _a;
+ return __awaiter(this, void 0, void 0, function () {
+ var _p, _b;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ _p = [(_a = this._createConfig) === null || _a === void 0 ? void 0 : _a.linkParams, params].filter(function (x) { return x; }).join('&');
+ _b = "https://get.openwa.dev/l/".concat;
+ return [4 /*yield*/, this.getHostNumber()];
+ case 1: return [2 /*return*/, _b.apply("https://get.openwa.dev/l/", [_c.sent()]).concat(_p ? "?".concat(_p) : '')];
+ }
+ });
+ });
+ };
+ /**
+ * Generate a license link
+ */
+ Client.prototype.getLicenseLink = function (params) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.link(params)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * {@license:restricted@}
+ *
+ * Sends a text message to given chat
+ *
+ * A license is **NOT** required to send messages with existing chats/contacts. A license is only required for starting conversations with new numbers.
+ *
+ * @param to chat id: `xxxxx@c.us`
+ * @param content text message
+ */
+ Client.prototype.sendText = function (to, content) {
+ return __awaiter(this, void 0, void 0, function () {
+ var err, res, msg, _a, _b;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ if (!content)
+ content = '';
+ if (typeof content !== 'string')
+ content = String(content);
+ err = [
+ 'Not able to send message to broadcast',
+ 'Not a contact',
+ 'Error: Number not linked to WhatsApp Account',
+ 'ERROR: Please make sure you have at least one chat'
+ ];
+ content = (content === null || content === void 0 ? void 0 : content.trim()) || content;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, content = _a.content;
+ WAPI.sendSeen(to);
+ return WAPI.sendMessage(to, content);
+ }, { to: to, content: content })];
+ case 1:
+ res = _c.sent();
+ if (!err.includes(res)) return [3 /*break*/, 4];
+ msg = res;
+ if (!(res == err[1])) return [3 /*break*/, 3];
+ _b = (_a = "ERROR: ".concat(res, ". Unlock this feature and support open-wa by getting a license: ")).concat;
+ return [4 /*yield*/, this.link()];
+ case 2:
+ msg = _b.apply(_a, [_c.sent()]);
+ _c.label = 3;
+ case 3:
+ console.error("\n".concat(msg, "\n"));
+ return [2 /*return*/, this.responseWrap(msg)];
+ case 4: return [2 /*return*/, (err.includes(res) ? false : res)];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a text message to given chat that includes mentions.
+ * In order to use this method correctly you will need to send the text like this:
+ * "@4474747474747 how are you?"
+ * Basically, add a @ symbol before the number of the contact you want to mention.
+ *
+ * @param to chat id: `xxxxx@c.us`
+ * @param content text message
+ * @param hideTags Removes all tags within the message
+ * @param mentions You can optionally add an array of contact IDs to tag only specific people
+ */
+ Client.prototype.sendTextWithMentions = function (to, content, hideTags, mentions) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ //remove all @c.us from the content
+ content = content.replace(/@c.us/, "");
+ return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, content = _a.content, hideTags = _a.hideTags, mentions = _a.mentions;
+ WAPI.sendSeen(to);
+ return WAPI.sendMessageWithMentions(to, content, hideTags, mentions);
+ }, { to: to, content: content, hideTags: hideTags, mentions: mentions })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * NOTE: This is experimental, most accounts do not have access to this feature in their apps.
+ *
+ * Edit an existing message
+ *
+ * @param messageId The message ID to edit
+ * @param text The new text content
+ * @returns
+ */
+ Client.prototype.editMessage = function (messageId, text) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var messageId = _a.messageId, text = _a.text;
+ return WAPI.editMessage(messageId, text);
+ }, { messageId: messageId, text: text })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * [UNTESTED - REQUIRES FEEDBACK]
+ * Sends a payment request message to given chat
+ *
+ * @param to chat id: `xxxxx@c.us`
+ * @param amount number the amount to request in 1000 format (e.g £10 => 10000)
+ * @param currency string The 3 letter currency code
+ * @param message string optional message to send with the payment request
+ */
+ Client.prototype.sendPaymentRequest = function (to, amount, currency, message) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, amount = _a.amount, currency = _a.currency, message = _a.message;
+ return WAPI.sendPaymentRequest(to, amount, currency, message);
+ }, { to: to, amount: amount, currency: currency, message: message })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * @deprecated
+ *
+ * :::danger
+ *
+ * WA BIZ accounts CANNOT send buttons. This is a WA limitation. DO NOT get a license solely for access to buttons on wa business accounts.
+ * THIS IS NOT WORKING FOR GROUPS YET.
+ *
+ * BUTTONS ARE DEPRECATED FOR NOW. DO NOT GET A LICENSE TO USE BUTTONS.
+ *
+ * :::
+ *
+ * Send generic quick reply buttons. This is an insiders feature for MD accounts.
+ *
+ * @param {ChatId} to chat id
+ * @param {string | LocationButtonBody} body The body of the buttons message
+ * @param {Button[]} buttons Array of buttons - limit is 3!
+ * @param {string} title The title/header of the buttons message
+ * @param {string} footer The footer of the buttons message
+ */
+ Client.prototype.sendButtons = function (to, body, buttons, title, footer) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, body = _a.body, buttons = _a.buttons, title = _a.title, footer = _a.footer;
+ return WAPI.sendButtons(to, body, buttons, title, footer);
+ }, { to: to, body: body, buttons: buttons, title: title, footer: footer })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * @deprecated
+ *
+ * :::danger
+ *
+ * Template messages (URL & CALL buttons) are broken for the foreseeable future. Please DO NOT get a license solely for access to URL or CALL buttons. They are no longer reliable due to recent changes at WA.
+ * WA BIZ accounts CANNOT send buttons. This is a WA limitation. DO NOT get a license solely for access to buttons on wa business accounts.
+ *
+ * THIS IS NOT WORKING FOR GROUPS YET.
+ *
+ * ADVANCED ARE DEPRECATED FOR NOW. DO NOT GET A LICENSE TO USE BUTTONS.
+ *
+ * :::
+ *
+ *
+ * Send advanced buttons with media body. This is an insiders feature for MD accounts.
+ *
+ * Body can be location, image, video or document. Buttons can be quick reply, url or call buttons.
+ *
+ * @param {ChatId} to chat id
+ * @param {string | LocationButtonBody} body The body of the buttons message
+ * @param {AdvancedButton[]} buttons Array of buttons - limit is 3!
+ * @param {string} title The title/header of the buttons message
+ * @param {string} footer The footer of the buttons message
+ * @param {string} filename Required if body is a file!!
+ */
+ Client.prototype.sendAdvancedButtons = function (to, body, buttons, text, footer, filename) {
+ return __awaiter(this, void 0, void 0, function () {
+ var relativePath;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!(typeof body !== "string" && body.lat)) return [3 /*break*/, 1];
+ //this is a location body
+ // eslint-disable-next-line no-self-assign
+ body = body;
+ return [3 /*break*/, 8];
+ case 1:
+ if (!(typeof body == "string" && !(0, tools_1.isDataURL)(body) && !(0, tools_1.isBase64)(body) && !body.includes("data:"))) return [3 /*break*/, 7];
+ relativePath = path.join(path.resolve(process.cwd(), body || ''));
+ if (!(typeof body == "string" && fs.existsSync(body) || fs.existsSync(relativePath))) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, datauri_1["default"])(fs.existsSync(body) ? body : relativePath)];
+ case 2:
+ body = _a.sent();
+ return [3 /*break*/, 6];
+ case 3:
+ if (!(typeof body == "string" && (0, is_url_superb_1["default"])(body))) return [3 /*break*/, 5];
+ return [4 /*yield*/, (0, tools_1.getDUrl)(body)];
+ case 4:
+ body = _a.sent();
+ return [3 /*break*/, 6];
+ case 5: throw new errors_1.CustomError(errors_1.ERROR_NAME.FILE_NOT_FOUND, "Cannot find file. Make sure the file reference is relative, a valid URL or a valid DataURL: ".concat(body.slice(0, 25)));
+ case 6: return [3 /*break*/, 8];
+ case 7:
+ if (typeof body == "string" && (body.includes("data:") && body.includes("undefined") || body.includes("application/octet-stream") && filename && mime_types_1["default"].lookup(filename))) {
+ body = "data:".concat(mime_types_1["default"].lookup(filename), ";base64,").concat(body.split(',')[1]);
+ }
+ _a.label = 8;
+ case 8: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, body = _a.body, buttons = _a.buttons, text = _a.text, footer = _a.footer, filename = _a.filename;
+ return WAPI.sendAdvancedButtons(to, body, buttons, text, footer, filename);
+ }, { to: to, body: body, buttons: buttons, text: text, footer: footer, filename: filename })];
+ case 9: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Send a banner image
+ *
+ * Note this is a bit of hack on top of a location message. During testing it is shown to not work on iPhones.
+ *
+ * @param {ChatId} to
+ * @param {Base64} base64 base64 encoded jpeg
+ */
+ Client.prototype.sendBanner = function (to, base64) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, base64 = _a.base64;
+ return WAPI.sendBanner(to, base64);
+ }, { to: to, base64: base64 })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * @deprecated
+ *
+ * :::danger
+ *
+ * It is not currently possible to send a listmessage to a group chat. This is a WA limitation.
+ * Please DO NOT get a license solely for access to list messages in group chats.
+ *
+ * LIST MESSAGES ARE DEPRECATED TILL FURTHER NOTICE
+ *
+ * :::
+ *
+ * Send a list message. This will not work when being sent from business accounts!
+ *
+ * @param {ChatId} to
+ * @param {Section[]} sections The Sections of rows for the list message
+ * @param {string} title The title of the list message
+ * @param {string} description The description of the list message
+ * @param {string} actionText The action text of the list message
+ */
+ Client.prototype.sendListMessage = function (to, sections, title, description, actionText) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, sections = _a.sections, title = _a.title, description = _a.description, actionText = _a.actionText;
+ return WAPI.sendListMessage(to, sections, title, description, actionText);
+ }, { to: to, sections: sections, title: title, description: description, actionText: actionText })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a reply to given chat that includes mentions, replying to the provided replyMessageId.
+ * In order to use this method correctly you will need to send the text like this:
+ * "@4474747474747 how are you?"
+ * Basically, add a @ symbol before the number of the contact you want to mention.
+ * @param to chat id: `xxxxx@c.us`
+ * @param content text message
+ * @param replyMessageId id of message to reply to
+ * @param hideTags Removes all tags within the message
+ * @param mentions You can optionally add an array of contact IDs to tag only specific people
+ */
+ Client.prototype.sendReplyWithMentions = function (to, content, replyMessageId, hideTags, mentions) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ //remove all @c.us from the content
+ content = content.replace(/@c.us/, "");
+ return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, content = _a.content, replyMessageId = _a.replyMessageId, hideTags = _a.hideTags, mentions = _a.mentions;
+ WAPI.sendSeen(to);
+ return WAPI.sendReplyWithMentions(to, content, replyMessageId, hideTags, mentions);
+ }, { to: to, content: content, replyMessageId: replyMessageId, hideTags: hideTags, mentions: mentions })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Tags everyone in the group with a message
+ *
+ * @param groupId group chat id: `xxxxx@g.us`
+ * @param content text message to add under all of the tags
+ * @param hideTags Removes all tags within the message
+ * @param formatting The formatting of the tags. Use @mention to indicate the actual tag. @default `@mention `
+ * @param messageBeforeTags set to `true` to show the message before all of the tags
+ * @returns `Promise`
+ */
+ Client.prototype.tagEveryone = function (groupId, content, hideTags, formatting, messageBeforeTags) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, content = _a.content, hideTags = _a.hideTags, formatting = _a.formatting, messageBeforeTags = _a.messageBeforeTags;
+ return WAPI.tagEveryone(groupId, content, hideTags, formatting, messageBeforeTags);
+ }, { groupId: groupId, content: content, hideTags: hideTags, formatting: formatting, messageBeforeTags: messageBeforeTags })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a link to a chat that includes a link preview.
+ * @param thumb The base 64 data of the image you want to use as the thunbnail. This should be no more than 200x200px. Note: Dont need data url on this param
+ * @param url The link you want to send
+ * @param title The title of the link
+ * @param description The long description of the link preview
+ * @param text The text you want to inslude in the message section. THIS HAS TO INCLUDE THE URL otherwise the url will be prepended to the text automatically.
+ * @param chatId The chat you want to send this message to.
+ * @param quotedMsgId [INSIDERS] Send this link preview message in response to a given quoted message
+ * @param customSize [INSIDERS] Anchor the size of the thumbnail
+ */
+ Client.prototype.sendMessageWithThumb = function (thumb, url, title, description, text, chatId, quotedMsgId, customSize) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var thumb = _a.thumb, url = _a.url, title = _a.title, description = _a.description, text = _a.text, chatId = _a.chatId, quotedMsgId = _a.quotedMsgId, customSize = _a.customSize;
+ WAPI.sendMessageWithThumb(thumb, url, title, description, text, chatId, quotedMsgId, customSize);
+ }, {
+ thumb: thumb,
+ url: url,
+ title: title,
+ description: description,
+ text: text,
+ chatId: chatId,
+ quotedMsgId: quotedMsgId,
+ customSize: customSize
+ })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Note: `address` and `url` are parameters available to insiders only.
+ *
+ * Sends a location message to given chat
+ * @param to chat id: `xxxxx@c.us`
+ * @param lat latitude: '51.5074'
+ * @param lng longitude: '0.1278'
+ * @param loc location text: 'LONDON!'
+ * @param address address text: '1 Regents Park!'
+ * @param url address text link: 'https://example.com'
+ */
+ Client.prototype.sendLocation = function (to, lat, lng, loc, address, url) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, lat = _a.lat, lng = _a.lng, loc = _a.loc, address = _a.address, url = _a.url;
+ return WAPI.sendLocation(to, lat, lng, loc, address, url);
+ }, { to: to, lat: lat, lng: lng, loc: loc, address: address, url: url })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get the generated user agent, this is so you can send it to the decryption module.
+ * @returns String useragent of wa-web session
+ */
+ Client.prototype.getGeneratedUserAgent = function (userA) {
+ return __awaiter(this, void 0, void 0, function () {
+ var ua;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ ua = userA || puppeteer_config_1.useragent;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var ua = _a.ua;
+ return WAPI.getGeneratedUserAgent(ua);
+ }, { ua: ua })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Decrypts a media message.
+ * @param message This can be the serialized [[MessageId]] or the whole [[Message]] object. It is advised to just use the serialized message ID.
+ * @returns `Promise<[[DataURL]]>`
+ */
+ Client.prototype.decryptMedia = function (message) {
+ return __awaiter(this, void 0, void 0, function () {
+ var m, _a, _b, _c, mediaData;
+ return __generator(this, function (_d) {
+ switch (_d.label) {
+ case 0:
+ if (!(typeof message === "string")) return [3 /*break*/, 2];
+ return [4 /*yield*/, this.getMessageById(message)];
+ case 1:
+ m = _d.sent();
+ return [3 /*break*/, 3];
+ case 2:
+ m = message;
+ _d.label = 3;
+ case 3:
+ if (!m.mimetype)
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.NOT_MEDIA, "Not a media message");
+ if (!(m.type == "sticker")) return [3 /*break*/, 5];
+ return [4 /*yield*/, this.getStickerDecryptable(m.id)];
+ case 4:
+ m = _d.sent();
+ _d.label = 5;
+ case 5:
+ if (!(m === false)) return [3 /*break*/, 7];
+ _b = (_a = console).error;
+ _c = "\nUnable to decrypt sticker. Unlock this feature and support open-wa by getting a license: ".concat;
+ return [4 /*yield*/, this.link("v=i")];
+ case 6:
+ _b.apply(_a, [_c.apply("\nUnable to decrypt sticker. Unlock this feature and support open-wa by getting a license: ", [_d.sent(), "\n"])]);
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.STICKER_NOT_DECRYPTED, 'Sticker not decrypted');
+ case 7: return [4 /*yield*/, (0, wa_decrypt_1.decryptMedia)(m)];
+ case 8:
+ mediaData = _d.sent();
+ return [2 /*return*/, "data:".concat(m.mimetype, ";base64,").concat(mediaData.toString('base64'))];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a image to given chat, with caption or not, using base64
+ * @param to chat id `xxxxx@c.us`
+ * @param file DataURL data:image/xxx;base64,xxx or the RELATIVE (should start with `./` or `../`) path of the file you want to send. With the latest version, you can now set this to a normal URL (for example [GET] `https://file-examples-com.github.io/uploads/2017/10/file_example_JPG_2500kB.jpg`).
+ * @param filename string xxxxx
+ * @param caption string xxxxx
+ * @param waitForKey boolean default: false set this to true if you want to wait for the id of the message. By default this is set to false as it will take a few seconds to retrieve to the key of the message and this waiting may not be desirable for the majority of users.
+ * @param hideTags boolean default: false [INSIDERS] set this to try silent tag someone in the caption
+ * @returns `Promise ` This will either return true or the id of the message. It will return true after 10 seconds even if waitForId is true
+ */
+ Client.prototype.sendImage = function (to, file, filename, caption, quotedMsgId, waitForId, ptt, withoutPreview, hideTags, viewOnce, requestConfig) {
+ return __awaiter(this, void 0, void 0, function () {
+ var err, _a, _b, inputElementId, inputElement, fileAsLocalTemp, res;
+ var _this = this;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ err = [
+ 'Not able to send message to broadcast',
+ 'Not a contact',
+ 'Error: Number not linked to WhatsApp Account',
+ 'ERROR: Please make sure you have at least one chat'
+ ];
+ return [4 /*yield*/, Promise.all([
+ (function () { return __awaiter(_this, void 0, void 0, function () {
+ var inputElementId, inputElement;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.createTemporaryFileInput(); })];
+ case 1:
+ inputElementId = _a.sent();
+ return [4 /*yield*/, this._page.$("#".concat(inputElementId))];
+ case 2:
+ inputElement = _a.sent();
+ return [2 /*return*/, [inputElementId, inputElement]];
+ }
+ });
+ }); })(),
+ (0, tools_1.assertFile)(file, filename, tools_1.FileOutputTypes.TEMP_FILE_PATH, requestConfig || {})
+ ])
+ //@ts-ignore
+ ];
+ case 1:
+ _a = _c.sent(), _b = _a[0], inputElementId = _b[0], inputElement = _b[1], fileAsLocalTemp = _a[1];
+ //@ts-ignore
+ return [4 /*yield*/, inputElement.uploadFile(fileAsLocalTemp)];
+ case 2:
+ //@ts-ignore
+ _c.sent();
+ file = inputElementId;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, file = _a.file, filename = _a.filename, caption = _a.caption, quotedMsgId = _a.quotedMsgId, waitForId = _a.waitForId, ptt = _a.ptt, withoutPreview = _a.withoutPreview, hideTags = _a.hideTags, viewOnce = _a.viewOnce;
+ return WAPI.sendImage(file, to, filename, caption, quotedMsgId, waitForId, ptt, withoutPreview, hideTags, viewOnce);
+ }, { to: to, file: file, filename: filename, caption: caption, quotedMsgId: quotedMsgId, waitForId: waitForId, ptt: ptt, withoutPreview: withoutPreview, hideTags: hideTags, viewOnce: viewOnce })];
+ case 3:
+ res = _c.sent();
+ if (!fileAsLocalTemp) return [3 /*break*/, 5];
+ return [4 /*yield*/, (0, tools_1.rmFileAsync)(fileAsLocalTemp)];
+ case 4:
+ _c.sent();
+ _c.label = 5;
+ case 5:
+ if (err.includes(res))
+ console.error(res);
+ return [2 /*return*/, (err.includes(res) ? false : res)];
+ }
+ });
+ });
+ };
+ /**
+ * Automatically sends a youtube link with the auto generated link preview. You can also add a custom message.
+ * @param chatId
+ * @param url string A youtube link.
+ * @param text string Custom text as body of the message, this needs to include the link or it will be appended after the link.
+ * @param thumbnail string Base64 of the jpeg/png which will be used to override the automatically generated thumbnail.
+ * @param quotedMsgId [INSIDERS] Send this link preview message in response to a given quoted message
+ * @param customSize [INSIDERS] Anchor the size of the thumbnail
+ */
+ Client.prototype.sendYoutubeLink = function (to, url, text, thumbnail, quotedMsgId, customSize) {
+ if (text === void 0) { text = ''; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.sendLinkWithAutoPreview(to, url, text, thumbnail, quotedMsgId, customSize)];
+ });
+ });
+ };
+ /**
+ * Automatically sends a link with the auto generated link preview. You can also add a custom message.
+ * @param chatId
+ * @param url string A link.
+ * @param text string Custom text as body of the message, this needs to include the link or it will be appended after the link.
+ * @param thumbnail Base64 of the jpeg/png which will be used to override the automatically generated thumbnail.
+ * @param quotedMsgId [INSIDERS] Send this link preview message in response to a given quoted message
+ * @param customSize [INSIDERS] Anchor the size of the thumbnail
+ */
+ Client.prototype.sendLinkWithAutoPreview = function (to, url, text, thumbnail, quotedMsgId, customSize) {
+ var _a;
+ return __awaiter(this, void 0, void 0, function () {
+ var linkData, thumb, error_4;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ _b.trys.push([0, 4, , 5]);
+ return [4 /*yield*/, axios_1["default"].get("".concat(((_a = this._createConfig) === null || _a === void 0 ? void 0 : _a.linkParser) || "https://link.openwa.cloud/api", "?url=").concat(url))];
+ case 1:
+ linkData = (_b.sent()).data;
+ logging_1.log.info("Got link data");
+ if (!!thumbnail) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, tools_1.getDUrl)(linkData.image)];
+ case 2:
+ thumb = _b.sent();
+ _b.label = 3;
+ case 3: return [3 /*break*/, 5];
+ case 4:
+ error_4 = _b.sent();
+ console.error(error_4);
+ return [3 /*break*/, 5];
+ case 5:
+ if (!(linkData && (thumbnail || thumb))) return [3 /*break*/, 7];
+ return [4 /*yield*/, this.sendMessageWithThumb(thumbnail || thumb, url, linkData.title, linkData.description, text, to, quotedMsgId, customSize)];
+ case 6: return [2 /*return*/, _b.sent()];
+ case 7: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, url = _a.url, text = _a.text, thumbnail = _a.thumbnail;
+ return WAPI.sendLinkWithAutoPreview(to, url, text, thumbnail);
+ }, { to: to, url: url, text: text, thumbnail: thumbnail })];
+ case 8: return [2 /*return*/, _b.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Sends a reply to a given message. Please note, you need to have at least sent one normal message to a contact in order for this to work properly.
+ *
+ * @param to string chatid
+ * @param content string reply text
+ * @param quotedMsgId string the msg id to reply to.
+ * @param sendSeen boolean If set to true, the chat will 'blue tick' all messages before sending the reply
+ * @returns `Promise` false if didn't work, otherwise returns message id.
+ */
+ Client.prototype.reply = function (to, content, quotedMsgId, sendSeen) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!sendSeen) return [3 /*break*/, 2];
+ return [4 /*yield*/, this.sendSeen(to)];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, content = _a.content, quotedMsgId = _a.quotedMsgId;
+ return WAPI.reply(to, content, quotedMsgId);
+ }, { to: to, content: content, quotedMsgId: quotedMsgId })];
+ case 3: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Check if a recipient has read receipts on.
+ *
+ * This will only work if you have chats sent back and forth between you and the contact 1-1.
+ *
+ * @param contactId The Id of the contact with which you have an existing conversation with messages already.
+ * @returns `Promise` true or false or a string with an explaintaion of why it wasn't able to determine the read receipts.
+ *
+ */
+ Client.prototype.checkReadReceipts = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var contactId = _a.contactId;
+ return WAPI.checkReadReceipts(contactId);
+ }, { contactId: contactId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a file to given chat, with caption or not, using base64. This is exactly the same as sendImage
+ *
+ * Please note that any file that resolves to mime-type `octet-stream` will, by default, resolve to an MP4 file.
+ *
+ * If you want a specific filetype, then explcitly select the correct mime-type from https://www.iana.org/assignments/media-types/media-types.xhtml
+ *
+ *
+ * @param to chat id `xxxxx@c.us`
+ * @param file DataURL data:image/xxx;base64,xxx or the RELATIVE (should start with `./` or `../`) path of the file you want to send. With the latest version, you can now set this to a normal URL (for example [GET] `https://file-examples-com.github.io/uploads/2017/10/file_example_JPG_2500kB.jpg`).
+ * @param filename string xxxxx
+ * @param caption string xxxxx With an [INSIDERS LICENSE-KEY](https://gum.co/open-wa?tier=Insiders%20Program) you can also tag people in groups with `@[number]`. For example if you want to mention the user with the number `44771234567`, just add `@44771234567` in the caption.
+ * @param quotedMsgId string true_0000000000@c.us_JHB2HB23HJ4B234HJB to send as a reply to a message
+ * @param waitForId boolean default: false set this to true if you want to wait for the id of the message. By default this is set to false as it will take a few seconds to retrieve to the key of the message and this waiting may not be desirable for the majority of users.
+ * @param ptt boolean default: false set this to true if you want to send the file as a push to talk file.
+ * @param withoutPreview boolean default: false set this to true if you want to send the file without a preview (i.e as a file). This is useful for preventing auto downloads on recipient devices.
+ * @param hideTags boolean default: false [INSIDERS] set this to try silent tag someone in the caption
+ * @returns `Promise ` This will either return true or the id of the message. It will return true after 10 seconds even if waitForId is true
+ */
+ Client.prototype.sendFile = function (to, file, filename, caption, quotedMsgId, waitForId, ptt, withoutPreview, hideTags, viewOnce, requestConfig) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.sendImage(to, file, filename, caption, quotedMsgId, waitForId, ptt, withoutPreview, hideTags, viewOnce, requestConfig)];
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Checks whether or not the group id provided is known to be unsafe by the contributors of the library.
+ * @param groupChatId The group chat you want to deteremine is unsafe
+ * @returns `Promise ` This will either return a boolean indiciating whether this group chat id is considered unsafe or an error message as a string
+ */
+ Client.prototype.isGroupIdUnsafe = function (groupChatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ var data;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, axios_1["default"].post('https://openwa.dev/groupId-check', {
+ groupChatId: groupChatId,
+ sessionInfo: this.getSessionInfo(),
+ config: this.getConfig()
+ })];
+ case 1:
+ data = (_a.sent()).data;
+ if (data.unsafe)
+ console.warn("".concat(groupChatId, " is marked as unsafe"));
+ return [2 /*return*/, data.err || data.unsafe];
+ }
+ });
+ });
+ };
+ /**
+ * Attempts to send a file as a voice note. Useful if you want to send an mp3 file.
+ * @param to chat id `xxxxx@c.us`
+ * @param file base64 data:image/xxx;base64,xxx or the path of the file you want to send.
+ * @param quotedMsgId string true_0000000000@c.us_JHB2HB23HJ4B234HJB to send as a reply to a message
+ * @returns `Promise ` This will either return true or the id of the message. It will return true after 10 seconds even if waitForId is true
+ */
+ Client.prototype.sendPtt = function (to, file, quotedMsgId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.sendImage(to, file, 'ptt.ogg', '', quotedMsgId ? quotedMsgId : null, true, true)];
+ });
+ });
+ };
+ /**
+ * Send an audio file with the default audio player (not PTT/voice message)
+ * @param to chat id `xxxxx@c.us`
+ * @param base64 base64 data:image/xxx;base64,xxx or the path of the file you want to send.
+ * @param quotedMsgId string true_0000000000@c.us_JHB2HB23HJ4B234HJB to send as a reply to a message
+ */
+ Client.prototype.sendAudio = function (to, file, quotedMsgId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this.sendFile(to, file, 'file.mp3', '', quotedMsgId, true, false, false, false)];
+ });
+ });
+ };
+ /**
+ * Send a poll to a group chat
+ * @param to chat id - a group chat is required
+ * @param name the name of the poll
+ * @param options an array of poll options
+ * @param quotedMsgId A message to quote when sending the poll
+ * @param allowMultiSelect Whether or not to allow multiple selections. default false
+ */
+ Client.prototype.sendPoll = function (to, name, options, quotedMsgId, allowMultiSelect) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, name = _a.name, options = _a.options, quotedMsgId = _a.quotedMsgId, allowMultiSelect = _a.allowMultiSelect;
+ return WAPI.sendPoll(to, name, options, quotedMsgId, allowMultiSelect);
+ }, { to: to, name: name, options: options, quotedMsgId: quotedMsgId, allowMultiSelect: allowMultiSelect })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a video to given chat as a gif, with caption or not, using base64
+ * @param to chat id `xxxxx@c.us`
+ * @param file DataURL data:image/xxx;base64,xxx or the RELATIVE (should start with `./` or `../`) path of the file you want to send. With the latest version, you can now set this to a normal URL (for example [GET] `https://file-examples-com.github.io/uploads/2017/10/file_example_JPG_2500kB.jpg`).
+ * @param filename string xxxxx
+ * @param caption string xxxxx
+ * @param quotedMsgId string true_0000000000@c.us_JHB2HB23HJ4B234HJB to send as a reply to a message
+ * @param requestConfig {} By default the request is a get request, however you can override that and many other options by sending this parameter. You can read more about this parameter here: https://github.com/axios/axios#request-config
+ */
+ Client.prototype.sendVideoAsGif = function (to, file, filename, caption, quotedMsgId, requestConfig) {
+ if (requestConfig === void 0) { requestConfig = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ var relativePath;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!!(0, tools_1.isDataURL)(file)) return [3 /*break*/, 5];
+ relativePath = path.join(path.resolve(process.cwd(), file || ''));
+ if (!(fs.existsSync(file) || fs.existsSync(relativePath))) return [3 /*break*/, 2];
+ return [4 /*yield*/, (0, datauri_1["default"])(fs.existsSync(file) ? file : relativePath)];
+ case 1:
+ file = _a.sent();
+ return [3 /*break*/, 5];
+ case 2:
+ if (!(0, is_url_superb_1["default"])(file)) return [3 /*break*/, 4];
+ return [4 /*yield*/, (0, tools_1.getDUrl)(file, requestConfig)];
+ case 3:
+ file = _a.sent();
+ return [3 /*break*/, 5];
+ case 4: throw new errors_1.CustomError(errors_1.ERROR_NAME.FILE_NOT_FOUND, 'Cannot find file. Make sure the file reference is relative, a valid URL or a valid DataURL');
+ case 5: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, file = _a.file, filename = _a.filename, caption = _a.caption, quotedMsgId = _a.quotedMsgId;
+ return WAPI.sendVideoAsGif(file, to, filename, caption, quotedMsgId);
+ }, { to: to, file: file, filename: filename, caption: caption, quotedMsgId: quotedMsgId })];
+ case 6: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a video to given chat as a gif by using a giphy link, with caption or not, using base64
+ * @param to chat id `xxxxx@c.us`
+ * @param giphyMediaUrl string https://media.giphy.com/media/oYtVHSxngR3lC/giphy.gif => https://i.giphy.com/media/oYtVHSxngR3lC/200w.mp4
+ * @param caption string xxxxx
+ */
+ Client.prototype.sendGiphy = function (to, giphyMediaUrl, caption) {
+ return __awaiter(this, void 0, void 0, function () {
+ var ue, n, r, filename, dUrl;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ ue = /^https?:\/\/media\.giphy\.com\/media\/([a-zA-Z0-9]+)/;
+ n = ue.exec(giphyMediaUrl);
+ if (!n) return [3 /*break*/, 3];
+ r = "https://i.giphy.com/".concat(n[1], ".mp4");
+ filename = "".concat(n[1], ".mp4");
+ return [4 /*yield*/, (0, tools_1.getDUrl)(r)];
+ case 1:
+ dUrl = _a.sent();
+ return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, dUrl = _a.dUrl, filename = _a.filename, caption = _a.caption;
+ WAPI.sendVideoAsGif(dUrl, to, filename, caption);
+ }, { to: to, dUrl: dUrl, filename: filename, caption: caption })];
+ case 2: return [2 /*return*/, _a.sent()];
+ case 3:
+ console.log('something is wrong with this giphy link');
+ logging_1.log.error('something is wrong with this giphy link', giphyMediaUrl);
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a file by Url or custom options
+ * @param to chat id `xxxxx@c.us`
+ * @param url string https://i.giphy.com/media/oYtVHSxngR3lC/200w.mp4
+ * @param filename string 'video.mp4'
+ * @param caption string xxxxx
+ * @param quotedMsgId string true_0000000000@c.us_JHB2HB23HJ4B234HJB to send as a reply to a message
+ * @param requestConfig {} By default the request is a get request, however you can override that and many other options by sending this parameter. You can read more about this parameter here: https://github.com/axios/axios#request-config
+ * @param waitForId boolean default: false set this to true if you want to wait for the id of the message. By default this is set to false as it will take a few seconds to retrieve to the key of the message and this waiting may not be desirable for the majority of users.
+ * @param ptt boolean default: false set this to true if you want to send the file as a push to talk file.
+ * @param withoutPreview boolean default: false set this to true if you want to send the file without a preview (i.e as a file). This is useful for preventing auto downloads on recipient devices.
+ */
+ Client.prototype.sendFileFromUrl = function (to, url, filename, caption, quotedMsgId, requestConfig, waitForId, ptt, withoutPreview, hideTags, viewOnce) {
+ if (requestConfig === void 0) { requestConfig = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.sendFile(to, url, filename, caption, quotedMsgId, waitForId, ptt, withoutPreview, hideTags, viewOnce, requestConfig)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns an object with all of your host device details
+ */
+ Client.prototype.getMe = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getMe(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns an object with properties of internal features and boolean values that represent if the respective feature is enabled or not.
+ */
+ Client.prototype.getFeatures = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.evaluate(function () { return WAPI.getFeatures(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns a PNG DataURL screenshot of the session
+ * @param chatId Chat ID to open before taking a snapshot
+ * @param width Width of the viewport for the snapshot. Height also required if you want to resize.
+ * @param height Height of the viewport for the snapshot. Width also required if you want to resize.
+ * @returns `Promise`
+ */
+ Client.prototype.getSnapshot = function (chatId, width, height) {
+ return __awaiter(this, void 0, void 0, function () {
+ var snapshotElement, _a, screenshot;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (!(width && height)) return [3 /*break*/, 2];
+ return [4 /*yield*/, this.resizePage(width, height)];
+ case 1:
+ _b.sent();
+ _b.label = 2;
+ case 2:
+ if (!chatId) return [3 /*break*/, 4];
+ return [4 /*yield*/, this._page.evaluateHandle(function (_a) {
+ var chatId = _a.chatId;
+ return WAPI.getSnapshotElement(chatId);
+ }, { chatId: chatId })];
+ case 3:
+ _a = _b.sent();
+ return [3 /*break*/, 5];
+ case 4:
+ _a = this.getPage();
+ _b.label = 5;
+ case 5:
+ snapshotElement = _a;
+ return [4 /*yield*/, snapshotElement.screenshot({
+ type: "png",
+ encoding: "base64"
+ })];
+ case 6:
+ screenshot = _b.sent();
+ return [2 /*return*/, "data:image/png;base64,".concat(screenshot)];
+ }
+ });
+ });
+ };
+ /**
+ * Returns some metrics of the session/page.
+ * @returns `Promise`
+ */
+ Client.prototype.metrics = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var metrics, sessionMetrics, res;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._page.metrics()];
+ case 1:
+ metrics = _a.sent();
+ return [4 /*yield*/, this.pup(function () { return WAPI.launchMetrics(); })];
+ case 2:
+ sessionMetrics = _a.sent();
+ res = __assign(__assign({}, (metrics || {})), (sessionMetrics || {}));
+ logging_1.log.info("Metrics:", res);
+ return [2 /*return*/, res];
+ }
+ });
+ });
+ };
+ /**
+ * Returns an array of group ids where the host account is admin
+ */
+ Client.prototype.iAmAdmin = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.iAmAdmin(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns an array of group ids where the host account has been kicked
+ */
+ Client.prototype.getKickedGroups = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getKickedGroups(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Syncs contacts with phone. This promise does not resolve so it will instantly return true.
+ */
+ Client.prototype.syncContacts = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.syncContacts(); })];
+ case 1:
+ //@ts-ignore
+ return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Easily get the amount of messages loaded up in the session. This will allow you to determine when to clear chats/cache.
+ */
+ Client.prototype.getAmountOfLoadedMessages = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getAmountOfLoadedMessages(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Find any product listings of the given number. Use this to query a catalog
+ *
+ * @param id id of business profile (i.e the number with @c.us)
+ * @returns None
+ */
+ Client.prototype.getBusinessProfilesProducts = function (id) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var id = _a.id;
+ return WAPI.getBusinessProfilesProducts(id);
+ }, { id: id })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get the business info of a given contact id
+ *
+ * @param id id of business profile (i.e the number with @c.us)
+ * @returns None
+ */
+ Client.prototype.getBusinessProfile = function (id) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var id = _a.id;
+ return WAPI.getBusinessProfile(id);
+ }, { id: id })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends product with image to chat
+ * @param imgBase64 Base64 image data
+ * @param chatid string the id of the chat that you want to send this product to
+ * @param caption string the caption you want to add to this message
+ * @param bizNumber string the @c.us number of the business account from which you want to grab the product
+ * @param productId string the id of the product within the main catalog of the aforementioned business
+ * @returns
+ */
+ Client.prototype.sendImageWithProduct = function (to, image, caption, bizNumber, productId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, image = _a.image, bizNumber = _a.bizNumber, caption = _a.caption, productId = _a.productId;
+ WAPI.sendImageWithProduct(image, to, caption, bizNumber, productId);
+ }, { to: to, image: image, bizNumber: bizNumber, caption: caption, productId: productId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * @deprecated
+ * Feature Currently only available with Premium License accounts.
+ *
+ * Send a custom product to a chat. Please see [[CustomProduct]] for details.
+ *
+ * Caveats:
+ * - URL will not work (unable to click), you will have to send another message with the URL.
+ * - Recipient will see a thin banner under picture that says "Something went wrong"
+ * - This will only work if you have at least 1 product already in your catalog
+ * - Only works on Business accounts
+ */
+ Client.prototype.sendCustomProduct = function (to, image, productData) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, image = _a.image, productData = _a.productData;
+ return WAPI.sendCustomProduct(to, image, productData);
+ }, { to: to, image: image, productData: productData })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends contact card to given chat id. You can use this to send multiple contacts but they will show up as multiple single-contact messages.
+ * @param {string} to 'xxxx@c.us'
+ * @param {string|array} contact 'xxxx@c.us' | ['xxxx@c.us', 'yyyy@c.us', ...]
+ */
+ Client.prototype.sendContact = function (to, contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, contactId = _a.contactId;
+ return WAPI.sendContact(to, contactId);
+ }, { to: to, contactId: contactId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * {@license:insiders@}
+ *
+ * Sends multiple contacts as a single message
+ *
+ * @param to 'xxxx@c.us'
+ * @param contact ['xxxx@c.us', 'yyyy@c.us', ...]
+ */
+ Client.prototype.sendMultipleContacts = function (to, contactIds) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, contactIds = _a.contactIds;
+ return WAPI.sendMultipleContacts(to, contactIds);
+ }, { to: to, contactIds: contactIds })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Simulate '...typing' in chat
+ * @param {string} to 'xxxx@c.us'
+ * @param {boolean} on turn on similated typing, false to turn it off you need to manually turn this off.
+ */
+ Client.prototype.simulateTyping = function (to, on) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, on = _a.on;
+ return WAPI.simulateTyping(to, on);
+ }, { to: to, on: on })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Simulate '...recording' in chat
+ * @param {string} to 'xxxx@c.us'
+ * @param {boolean} on turn on similated recording, false to turn it off you need to manually turn this off.
+ */
+ Client.prototype.simulateRecording = function (to, on) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, on = _a.on;
+ return WAPI.simulateRecording(to, on);
+ }, { to: to, on: on })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * @param id The id of the conversation
+ * @param archive boolean true => archive, false => unarchive
+ * @return boolean true: worked, false: didnt work (probably already in desired state)
+ */
+ Client.prototype.archiveChat = function (id, archive) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var id = _a.id, archive = _a.archive;
+ return WAPI.archiveChat(id, archive);
+ }, { id: id, archive: archive })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Pin/Unpin chats
+ *
+ * @param id The id of the conversation
+ * @param pin boolean true => pin, false => unpin
+ * @return boolean true: worked
+ */
+ Client.prototype.pinChat = function (id, pin) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var id = _a.id, pin = _a.pin;
+ return WAPI.pinChat(id, pin);
+ }, { id: id, pin: pin })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Pin/Unpin message
+ *
+ * @param id The id of the message
+ * @param pin boolean true => pin, false => unpin
+ * @param pinDuration The length of time to pin the message. Default `ThirtyDays`
+ * @return boolean true: worked
+ */
+ Client.prototype.pinMessage = function (id, pin, pinDuration) {
+ if (pinDuration === void 0) { pinDuration = "ThirtyDays"; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var id = _a.id, pin = _a.pin, pinDuration = _a.pinDuration;
+ return WAPI.pinMessage(id, pin, pinDuration);
+ }, { id: id, pin: pin, pinDuration: pinDuration })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Keep a message inside an ephemeral chat
+ *
+ * @param id The id of the message
+ * @return boolean true: worked
+ */
+ Client.prototype.keepMessage = function (id, keep) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var id = _a.id, keep = _a.keep;
+ return WAPI.keepMessage(id, keep);
+ }, { id: id, keep: keep })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * {@license:insiders@}
+ *
+ * Mutes a conversation for a given duration. If already muted, this will update the muted duration. Mute durations are relative from when the method is called.
+ * @param chatId The id of the conversation you want to mute
+ * @param muteDuration ChatMuteDuration enum of the time you want this chat to be muted for.
+ * @return boolean true: worked or error code or message
+ */
+ Client.prototype.muteChat = function (chatId, muteDuration) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, muteDuration = _a.muteDuration;
+ return WAPI.muteChat(chatId, muteDuration);
+ }, { chatId: chatId, muteDuration: muteDuration })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Checks if a chat is muted
+ * @param chatId The id of the chat you want to check
+ * @returns boolean. `false` if the chat does not exist.
+ */
+ Client.prototype.isChatMuted = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId;
+ return WAPI.isChatMuted(chatId);
+ }, { chatId: chatId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * {@license:insiders@}
+ *
+ * Unmutes a conversation.
+ * @param id The id of the conversation you want to mute
+ * @return boolean true: worked or error code or message
+ */
+ Client.prototype.unmuteChat = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId;
+ return WAPI.unmuteChat(chatId);
+ }, { chatId: chatId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Forward an array of messages to a specific chat using the message ids or Objects
+ *
+ * @param to '000000000000@c.us'
+ * @param messages this can be any mixture of message ids or message objects
+ * @param skipMyMessages This indicates whether or not to skip your own messages from the array
+ */
+ Client.prototype.forwardMessages = function (to, messages, skipMyMessages) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, messages = _a.messages, skipMyMessages = _a.skipMyMessages;
+ return WAPI.forwardMessages(to, messages, skipMyMessages);
+ }, { to: to, messages: messages, skipMyMessages: skipMyMessages })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Ghost forwarding is like a normal forward but as if it were sent from the host phone [i.e it doesn't show up as forwarded.]
+ * Any potential abuse of this method will see it become paywalled.
+ * @param to: Chat id to forward the message to
+ * @param messageId: message id of the message to forward. Please note that if it is not loaded, this will return false - even if it exists.
+ * @returns `Promise`
+ */
+ Client.prototype.ghostForward = function (to, messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, messageId = _a.messageId;
+ return WAPI.ghostForward(to, messageId);
+ }, { to: to, messageId: messageId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves all contacts
+ * @returns array of [Contact]
+ */
+ Client.prototype.getAllContacts = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getAllContacts(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ Client.prototype.getWAVersion = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getWAVersion(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Generate a pre-filled github issue link to easily report a bug
+ */
+ Client.prototype.getIssueLink = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ return [2 /*return*/, (0, tools_1.generateGHIssueLink)(this.getConfig(), this.getSessionInfo())];
+ });
+ });
+ };
+ /**
+ * Retrieves if the phone is online. Please note that this may not be real time.
+ * @returns Boolean
+ */
+ Client.prototype.isConnected = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.isConnected(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Logs out from the session.
+ * @param preserveSessionData skip session.data.json file invalidation
+ * Please be careful when using this as it can exit the whole process depending on your config
+ */
+ Client.prototype.logout = function (preserveSessionData) {
+ if (preserveSessionData === void 0) { preserveSessionData = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!!preserveSessionData) return [3 /*break*/, 2];
+ logging_1.log.info("LOGOUT CALLED. INVALIDATING SESSION DATA");
+ return [4 /*yield*/, (0, browser_1.invalidateSesssionData)(this._createConfig)];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2: return [4 /*yield*/, this.pup(function () { return WAPI.logout(); })];
+ case 3: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * @deprecated No longer works due to multi-device changes
+ * Retrieves Battery Level
+ * @returns Number
+ */
+ Client.prototype.getBatteryLevel = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getBatteryLevel(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves whether or not phone is plugged in (i.e on charge)
+ * @returns Number
+ */
+ Client.prototype.getIsPlugged = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getIsPlugged(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves the host device number. Use this number when registering for a license key
+ * @returns Number
+ */
+ Client.prototype.getHostNumber = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var _a;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (!!this._hostAccountNumber) return [3 /*break*/, 2];
+ _a = this;
+ return [4 /*yield*/, this.pup(function () { return WAPI.getHostNumber(); })];
+ case 1:
+ _a._hostAccountNumber = (_b.sent());
+ _b.label = 2;
+ case 2: return [2 /*return*/, this._hostAccountNumber];
+ }
+ });
+ });
+ };
+ /**
+ * Returns the the type of license key used by the session.
+ * @returns
+ */
+ Client.prototype.getLicenseType = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getLicenseType(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * The EASY API uses this string to secure a subdomain on the openwa public tunnel service.
+ * @returns
+ */
+ Client.prototype.getTunnelCode = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var sessionId;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ sessionId = this.getSessionId();
+ return [4 /*yield*/, this.pup(function (sessionId) { return WAPI.getTunnelCode(sessionId); }, sessionId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get an array of chatIds with their respective last message's timestamp.
+ *
+ * This is useful for determining what chats are old/stale and need to be deleted.
+ */
+ Client.prototype.getLastMsgTimestamps = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getLastMsgTimestamps(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves all chats
+ * @returns array of [Chat]
+ */
+ Client.prototype.getAllChats = function (withNewMessageOnly) {
+ if (withNewMessageOnly === void 0) { withNewMessageOnly = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!withNewMessageOnly) return [3 /*break*/, 2];
+ return [4 /*yield*/, this.pup(function () {
+ return WAPI.getAllChatsWithNewMsg();
+ })];
+ case 1: return [2 /*return*/, _a.sent()];
+ case 2: return [4 /*yield*/, this.pup(function () { return WAPI.getAllChats(); })];
+ case 3: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * retrieves all Chat Ids
+ * @returns array of [ChatId]
+ */
+ Client.prototype.getAllChatIds = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getAllChatIds(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * retrieves an array of IDs of accounts blocked by the host account.
+ * @returns `Promise`
+ */
+ Client.prototype.getBlockedIds = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getBlockedIds(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * @deprecated
+ *
+ * Retrieves all chats with messages
+ *
+ * Please use `getAllUnreadMessages` instead of this to see all messages indicated by the green dots in the chat.
+ *
+ * @returns array of [Chat]
+ */
+ Client.prototype.getAllChatsWithMessages = function (withNewMessageOnly) {
+ if (withNewMessageOnly === void 0) { withNewMessageOnly = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ var _a, _b;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ _b = (_a = JSON).parse;
+ return [4 /*yield*/, this.pup(function (withNewMessageOnly) { return WAPI.getAllChatsWithMessages(withNewMessageOnly); }, withNewMessageOnly)];
+ case 1: return [2 /*return*/, _b.apply(_a, [_c.sent()])];
+ }
+ });
+ });
+ };
+ /**
+ * Returns a properly formatted array of messages from to send to the openai api
+ *
+ * @param last The amount of previous messages to retrieve. Defaults to 10
+ * @returns
+ */
+ Client.prototype.getGptArray = function (chatId, last) {
+ if (last === void 0) { last = 10; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, last = _a.last;
+ return WAPI.getGptArray(chatId, last);
+ }, { chatId: chatId, last: last })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieve all groups
+ * @returns array of groups
+ */
+ Client.prototype.getAllGroups = function (withNewMessagesOnly) {
+ if (withNewMessagesOnly === void 0) { withNewMessagesOnly = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (withNewMessagesOnly) { return WAPI.getAllGroups(withNewMessagesOnly); }, withNewMessagesOnly)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieve all commmunity Ids
+ * @returns array of group ids
+ */
+ Client.prototype.getAllCommunities = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getCommunities(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves group members as [Id] objects
+ * @param groupId group id
+ */
+ Client.prototype.getGroupMembersId = function (groupId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (groupId) { return WAPI.getGroupParticipantIDs(groupId); }, groupId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns the title and description of a given group id.
+ * @param groupId group id
+ */
+ Client.prototype.getGroupInfo = function (groupId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (groupId) { return WAPI.getGroupInfo(groupId); }, groupId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns the community metadata. Like group metadata but with a `subGroups` property which is the group metadata of the community subgroups.
+ * @param communityId community id
+ */
+ Client.prototype.getCommunityInfo = function (communityId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (communityId) { return WAPI.getCommunityInfo(communityId); }, communityId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Accepts a request from a recipient to join a group. Takes the message ID of the request message.
+ *
+ * @param {string} messageId
+ */
+ Client.prototype.acceptGroupJoinRequest = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.acceptGroupJoinRequest(messageId); }, messageId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves community members Ids
+ * @param communityId community id
+ */
+ Client.prototype.getCommunityParticipantIds = function (communityId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (communityId) { return WAPI.getCommunityParticipantIds(communityId); }, communityId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves community admin Ids
+ * @param communityId community id
+ */
+ Client.prototype.getCommunityAdminIds = function (communityId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (communityId) { return WAPI.getCommunityAdminIds(communityId); }, communityId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves community members as Contact objects
+ * @param communityId community id
+ */
+ Client.prototype.getCommunityParticipants = function (communityId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (communityId) { return WAPI.getCommunityParticipants(communityId); }, communityId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves community admins as Contact objects
+ * @param communityId community id
+ */
+ Client.prototype.getCommunityAdmins = function (communityId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (communityId) { return WAPI.getCommunityAdmins(communityId); }, communityId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /** Joins a group via the invite link, code, or message
+ * @param link This param is the string which includes the invite link or code. The following work:
+ * - Follow this link to join my WA group: https://chat.whatsapp.com/DHTGJUfFJAV9MxOpZO1fBZ
+ * - https://chat.whatsapp.com/DHTGJUfFJAV9MxOpZO1fBZ
+ * - DHTGJUfFJAV9MxOpZO1fBZ
+ *
+ * If you have been removed from the group previously, it will return `401`
+ *
+ * @param returnChatObj boolean When this is set to true and if the group was joined successfully, it will return a serialzed Chat object which includes group information and metadata. This is useful when you want to immediately do something with group metadata.
+ *
+ *
+ * @returns `Promise` Either false if it didn't work, or the group id.
+ */
+ Client.prototype.joinGroupViaLink = function (link, returnChatObj) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var link = _a.link, returnChatObj = _a.returnChatObj;
+ return WAPI.joinGroupViaLink(link, returnChatObj);
+ }, { link: link, returnChatObj: returnChatObj })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Block contact
+ * @param {string} id '000000000000@c.us'
+ */
+ Client.prototype.contactBlock = function (id) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (id) { return WAPI.contactBlock(id); }, id)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Report a contact for spam, block them and attempt to clear chat.
+ *
+ * @param {string} id '000000000000@c.us'
+ */
+ Client.prototype.reportSpam = function (id) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (id) { return WAPI.REPORTSPAM(id); }, id)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Unblock contact
+ * @param {string} id '000000000000@c.us'
+ */
+ Client.prototype.contactUnblock = function (id) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (id) { return WAPI.contactUnblock(id); }, id)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Removes the host device from the group
+ * @param groupId group id
+ */
+ Client.prototype.leaveGroup = function (groupId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (groupId) { return WAPI.leaveGroup(groupId); }, groupId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Extracts vcards from a message.This works on messages of typ `vcard` or `multi_vcard`
+ * @param msgId string id of the message to extract the vcards from
+ * @returns [vcard]
+ * ```
+ * [
+ * {
+ * displayName:"Contact name",
+ * vcard: "loong vcard string"
+ * }
+ * ]
+ * ```
+ * or false if no valid vcards found.
+ *
+ * Please use [vcf](https://www.npmjs.com/package/vcf) to convert a vcard string into a json object
+ */
+ Client.prototype.getVCards = function (msgId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (msgId) { return WAPI.getVCards(msgId); }, msgId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns group members [Contact] objects
+ * @param groupId
+ */
+ Client.prototype.getGroupMembers = function (groupId) {
+ return __awaiter(this, void 0, void 0, function () {
+ var membersIds, actions;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.getGroupMembersId(groupId)];
+ case 1:
+ membersIds = _a.sent();
+ logging_1.log.info("group members ids", membersIds);
+ if (!Array.isArray(membersIds)) {
+ console.error("group members ids is not an array", membersIds);
+ return [2 /*return*/, []];
+ }
+ actions = membersIds.map(function (memberId) {
+ return _this.getContact(memberId);
+ });
+ return [4 /*yield*/, Promise.all(actions)];
+ case 2: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves contact detail object of given contact id
+ * @param contactId
+ * @returns contact detial as promise
+ */
+ //@ts-ignore
+ Client.prototype.getContact = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.getContact(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves chat object of given contact id
+ * @param contactId
+ * @returns contact detial as promise
+ */
+ Client.prototype.getChatById = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.getChatById(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves message object of given message id
+ * @param messageId
+ * @returns message object
+ */
+ Client.prototype.getMessageById = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.getMessageById(messageId); }, messageId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Get the detailed message info for a group message sent out by the host account.
+ * @param messageId The message Id
+ */
+ Client.prototype.getMessageInfo = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.getMessageInfo(messageId); }, messageId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Retrieves an order object
+ * @param messageId or OrderId
+ * @returns order object
+ */
+ Client.prototype.getOrder = function (id) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (id) { return WAPI.getOrder(id); }, id)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Add a product to your catalog
+ *
+ * @param {string} name The name of the product
+ * @param {number} price The price of the product
+ * @param {string} currency The 3-letter currenct code for the product
+ * @param {string[]} images An array of dataurl or base64 strings of product images, the first image will be used as the main image. At least one image is required.
+ * @param {string} description optional, the description of the product
+ * @param {string} url The url of the product for more information
+ * @param {string} internalId The internal/backoffice id of the product
+ * @param {boolean} isHidden Whether or not the product is shown publicly in your catalog
+ * @returns product object
+ */
+ Client.prototype.createNewProduct = function (name, price, currency, images, description, url, internalId, isHidden) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!Array.isArray(images))
+ images = [images];
+ return [4 /*yield*/, Promise.all(images.map(function (image) { return (0, tools_1.ensureDUrl)(image); }))];
+ case 1:
+ images = _a.sent();
+ return [4 /*yield*/, this.pup(function (_a) {
+ var name = _a.name, price = _a.price, currency = _a.currency, images = _a.images, description = _a.description, url = _a.url, internalId = _a.internalId, isHidden = _a.isHidden;
+ return WAPI.createNewProduct(name, price, currency, images, description, url, internalId, isHidden);
+ }, { name: name, price: price, currency: currency, images: images, description: description, url: url, internalId: internalId, isHidden: isHidden })];
+ case 2: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Edit a product in your catalog
+ *
+ * @param {string} productId The catalog ID of the product
+ * @param {string} name The name of the product
+ * @param {number} price The price of the product
+ * @param {string} currency The 3-letter currenct code for the product
+ * @param {string[]} images An array of dataurl or base64 strings of product images, the first image will be used as the main image. At least one image is required.
+ * @param {string} description optional, the description of the product
+ * @param {string} url The url of the product for more information
+ * @param {string} internalId The internal/backoffice id of the product
+ * @param {boolean} isHidden Whether or not the product is shown publicly in your catalog
+ * @returns product object
+ */
+ Client.prototype.editProduct = function (productId, name, price, currency, images, description, url, internalId, isHidden) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var productId = _a.productId, name = _a.name, price = _a.price, currency = _a.currency, images = _a.images, description = _a.description, url = _a.url, internalId = _a.internalId, isHidden = _a.isHidden;
+ return WAPI.editProduct(productId, name, price, currency, images, description, url, internalId, isHidden);
+ }, { productId: productId, name: name, price: price, currency: currency, images: images, description: description, url: url, internalId: internalId, isHidden: isHidden })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Send a product to a chat
+ *
+ * @param {string} chatId The chatId
+ * @param {string} productId The id of the product
+ * @returns MessageID
+ */
+ Client.prototype.sendProduct = function (chatId, productId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, productId = _a.productId;
+ return WAPI.sendProduct(chatId, productId);
+ }, { chatId: chatId, productId: productId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Remove a product from the host account's catalog
+ *
+ * @param {string} productId The id of the product
+ * @returns boolean
+ */
+ Client.prototype.removeProduct = function (productId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var productId = _a.productId;
+ return WAPI.removeProduct(productId);
+ }, { productId: productId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves the last message sent by the host account in any given chat or globally.
+ * @param chatId This is optional. If no chat Id is set then the last message sent by the host account will be returned.
+ * @returns message object or `undefined` if the host account's last message could not be found.
+ */
+ Client.prototype.getMyLastMessage = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.getMyLastMessage(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves the starred messages in a given chat
+ * @param chatId Chat ID to filter starred messages by
+ * @returns message object
+ */
+ Client.prototype.getStarredMessages = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.getStarredMessages(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Star a message
+ * @param messageId Message ID of the message you want to star
+ * @returns `true`
+ */
+ Client.prototype.starMessage = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.starMessage(messageId); }, messageId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Unstar a message
+ * @param messageId Message ID of the message you want to unstar
+ * @returns `true`
+ */
+ Client.prototype.unstarMessage = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.unstarMessage(messageId); }, messageId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * React to a message
+ * @param messageId Message ID of the message you want to react to
+ * @param emoji 1 single emoji to add to the message as a reacion
+ * @returns boolean
+ */
+ Client.prototype.react = function (messageId, emoji) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var messageId = _a.messageId, emoji = _a.emoji;
+ return WAPI.react(messageId, emoji);
+ }, { messageId: messageId, emoji: emoji })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * @deprecated
+ *
+ * Retrieves a message object which results in a valid sticker instead of a blank one. This also works with animated stickers.
+ *
+ * If you run this without a valid insiders key, it will return false and cause an error upon decryption.
+ *
+ * @param messageId The message ID `message.id`
+ * @returns message object OR `false`
+ */
+ Client.prototype.getStickerDecryptable = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ var m;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.getStickerDecryptable(messageId); }, messageId)];
+ case 1:
+ m = _a.sent();
+ if (!m)
+ return [2 /*return*/, false];
+ return [2 /*return*/, __assign({ t: m.t, id: m.id }, (0, wa_decrypt_1.bleachMessage)(m))];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * {@license:insiders@}
+ *
+ * If a file is old enough, it will 404 if you try to decrypt it. This will allow you to force the host account to re upload the file and return a decryptable message.
+ *
+ * if you run this without a valid insiders key, it will return false and cause an error upon decryption.
+ *
+ * @param messageId
+ * @returns [[Message]] OR `false`
+ */
+ Client.prototype.forceStaleMediaUpdate = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ var m;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.forceStaleMediaUpdate(messageId); }, messageId)];
+ case 1:
+ m = _a.sent();
+ if (!m)
+ return [2 /*return*/, false];
+ return [2 /*return*/, __assign({}, (0, wa_decrypt_1.bleachMessage)(m))];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves chat object of given contact id
+ * @param contactId
+ * @returns contact detial as promise
+ */
+ Client.prototype.getChat = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.getChat(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Retrieves the groups that you have in common with a contact
+ * @param contactId
+ */
+ Client.prototype.getCommonGroups = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.getCommonGroups(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves the epoch timestamp of the time the contact was last seen. This will not work if:
+ * 1. They have set it so you cannot see their last seen via privacy settings.
+ * 2. You do not have an existing chat with the contact.
+ * 3. The chatId is for a group
+ * In both of those instances this method will return undefined.
+ * @param chatId The id of the chat.
+ * @returns number timestamp when chat was last online or undefined.
+ */
+ Client.prototype.getLastSeen = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.getLastSeen(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves chat picture
+ * @param chatId
+ * @returns Url of the chat picture or undefined if there is no picture for the chat.
+ */
+ Client.prototype.getProfilePicFromServer = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.getProfilePicFromServer(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sets a chat status to seen. Marks all messages as ack: 3
+ * @param chatId chat id: `xxxxx@c.us`
+ */
+ Client.prototype.sendSeen = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.sendSeen(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Runs sendSeen on all chats
+ */
+ Client.prototype.markAllRead = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.markAllRead(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sets a chat status to unread. May be useful to get host's attention
+ * @param chatId chat id: `xxxxx@c.us`
+ */
+ Client.prototype.markAsUnread = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.markAsUnread(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Checks if a chat contact is online. Not entirely sure if this works with groups.
+ *
+ * It will return `true` if the chat is `online`, `false` if the chat is `offline`, `PRIVATE` if the privacy settings of the contact do not allow you to see their status and `NO_CHAT` if you do not currently have a chat with that contact.
+ *
+ * @param chatId chat id: `xxxxx@c.us`
+ */
+ Client.prototype.isChatOnline = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.isChatOnline(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Load more messages in chat object from server. Use this in a while loop. This should return up to 50 messages at a time
+ * @param contactId
+ * @returns Message []
+ */
+ Client.prototype.loadEarlierMessages = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.loadEarlierMessages(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get the status of a contact
+ * @param contactId to '000000000000@c.us'
+ */
+ Client.prototype.getStatus = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.getStatus(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * {@license:insiders@}
+ *
+ * :::danger
+ *
+ * Buttons are broken for the foreseeable future. Please DO NOT get a license solely for access to buttons. They are no longer reliable due to recent changes at WA.
+ *
+ * :::
+ *
+ * Use a raw payload within your open-wa session
+ *
+ * @example
+ * If there is a code block, then both TypeDoc and VSCode will treat
+ * text outside of the code block as regular text.
+ *
+ * ```ts
+ * await B('44123456789@c.us', {
+ * test: 1
+ * })
+ * ```
+ * {@link loadAllEarlierMessages}
+ * @param chatId
+ * @param payload
+ * returns: MessageId
+ */
+ Client.prototype.B = function (chatId, payload) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, payload = _a.payload;
+ return WAPI.B(chatId, payload);
+ }, { chatId: chatId, payload: payload })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Load all messages in chat object from server.
+ * @param contactId
+ * @returns Message[]
+ */
+ Client.prototype.loadAllEarlierMessages = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.loadAllEarlierMessages(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Load all messages until a given timestamp in chat object from server.
+ * @param contactId
+ * @param timestamp in seconds
+ * @returns Message[]
+ */
+ Client.prototype.loadEarlierMessagesTillDate = function (contactId, timestamp) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var contactId = _a.contactId, timestamp = _a.timestamp;
+ return WAPI.loadEarlierMessagesTillDate(contactId, timestamp);
+ }, { contactId: contactId, timestamp: timestamp })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Delete the conversation from your WA
+ * @param chatId
+ * @returns boolean
+ */
+ Client.prototype.deleteChat = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.deleteConversation(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Delete all messages from the chat.
+ * @param chatId
+ * @returns boolean
+ */
+ Client.prototype.clearChat = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.clearChat(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves an invite link for a group chat. returns false if chat is not a group.
+ * @param chatId
+ * @returns `Promise`
+ */
+ Client.prototype.getGroupInviteLink = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.getGroupInviteLink(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get the details of a group through the invite link
+ * @param link This can be an invite link or invite code
+ * @returns
+ */
+ Client.prototype.inviteInfo = function (link) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (link) { return WAPI.inviteInfo(link); }, link)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Set/Unset a sticker as a fav.
+ * @param msgId The message Id related to the sticker you want to fav
+ * @param fav set this to true to fav a sticker, set it to false to remove the sticker from favorites. default true
+ * @returns favId The ID (filehash) of the fav sticker
+ */
+ Client.prototype.favSticker = function (msgId, fav) {
+ if (fav === void 0) { fav = true; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var msgId = _a.msgId, fav = _a.fav;
+ return WAPI.favSticker(msgId, fav);
+ }, { msgId: msgId, fav: fav })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Set/Unset a sticker as a fav.
+ * @param chatId The chat in which you want to send the sticker
+ * @param favId set this to true to favourite a sticker, set it to false to remove the sticker from favorites
+ * @returns MessageId of the sent sticker message
+ */
+ Client.prototype.sendFavSticker = function (chatId, favId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, favId = _a.favId;
+ return WAPI.sendFavSticker(chatId, favId);
+ }, { chatId: chatId, favId: favId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get an array of fav'ed stickers
+ */
+ Client.prototype.getFavStickers = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getFavStickers(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Revokes the current invite link for a group chat. Any previous links will stop working
+ * @param chatId
+ * @returns `Promise`
+ */
+ Client.prototype.revokeGroupInviteLink = function (chatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (chatId) { return WAPI.revokeGroupInviteLink(chatId); }, chatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Gets the contact IDs of members requesting approval to join the group
+ * @param groupChatId
+ * @returns `Promise`
+ */
+ Client.prototype.getGroupApprovalRequests = function (groupChatId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (groupChatId) { return WAPI.getGroupApprovalRequests(groupChatId); }, groupChatId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Approves a group join request
+ * @param groupChatId The group chat id
+ * @param contactId The contact id of the person who is requesting to join the group
+ * @returns `Promise`
+ */
+ Client.prototype.approveGroupJoinRequest = function (groupChatId, contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupChatId = _a.groupChatId, contactId = _a.contactId;
+ return WAPI.approveGroupJoinRequest(groupChatId, contactId);
+ }, { groupChatId: groupChatId, contactId: contactId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Rejects a group join request
+ * @param groupChatId The group chat id
+ * @param contactId The contact id of the person who is requesting to join the group
+ * @returns `Promise`
+ */
+ Client.prototype.rejectGroupJoinRequest = function (groupChatId, contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupChatId = _a.groupChatId, contactId = _a.contactId;
+ return WAPI.rejectGroupJoinRequest(groupChatId, contactId);
+ }, { groupChatId: groupChatId, contactId: contactId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Deletes message of given message id
+ * @param chatId The chat id from which to delete the message.
+ * @param messageId The specific message id of the message to be deleted
+ * @param onlyLocal If it should only delete locally (message remains on the other recipienct's phone). Defaults to false.
+ * @returns nothing
+ */
+ Client.prototype.deleteMessage = function (chatId, messageId, onlyLocal) {
+ if (onlyLocal === void 0) { onlyLocal = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, messageId = _a.messageId, onlyLocal = _a.onlyLocal;
+ return WAPI.smartDeleteMessages(chatId, messageId, onlyLocal);
+ }, { chatId: chatId, messageId: messageId, onlyLocal: onlyLocal })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Checks if a number is a valid WA number
+ * @param contactId, you need to include the @c.us at the end.
+ */
+ Client.prototype.checkNumberStatus = function (contactId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (contactId) { return WAPI.checkNumberStatus(contactId); }, contactId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves all unread Messages
+ * @param includeMe
+ * @param includeNotifications
+ * @param use_unread_count
+ * @returns any
+ */
+ Client.prototype.getUnreadMessages = function (includeMe, includeNotifications, use_unread_count) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var includeMe = _a.includeMe, includeNotifications = _a.includeNotifications, use_unread_count = _a.use_unread_count;
+ return WAPI.getUnreadMessages(includeMe, includeNotifications, use_unread_count);
+ }, { includeMe: includeMe, includeNotifications: includeNotifications, use_unread_count: use_unread_count })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves all new Messages. where isNewMsg==true
+ * @returns list of messages
+ */
+ Client.prototype.getAllNewMessages = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getAllNewMessages(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves all unread Messages. where ack==-1
+ * @returns list of messages
+ */
+ Client.prototype.getAllUnreadMessages = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getAllUnreadMessages(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves all unread Messages as indicated by the red dots in WA web. This returns an array of objects and are structured like so:
+ * ```javascript
+ * [{
+ * "id": "000000000000@g.us", //the id of the chat
+ * "indicatedNewMessages": [] //array of messages, not including any messages by the host phone
+ * }]
+ * ```
+ * @returns list of messages
+ */
+ Client.prototype.getIndicatedNewMessages = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var _a, _b;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ _b = (_a = JSON).parse;
+ return [4 /*yield*/, this.pup(function () { return WAPI.getIndicatedNewMessages(); })];
+ case 1: return [2 /*return*/, _b.apply(_a, [_c.sent()])];
+ }
+ });
+ });
+ };
+ /**
+ * Fires all unread messages to the onMessage listener.
+ * Make sure to call this AFTER setting your listeners.
+ * @returns array of message IDs
+ */
+ Client.prototype.emitUnreadMessages = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.emitUnreadMessages(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retrieves all Messages in a chat that have been loaded within the WA web instance.
+ *
+ * This does not load every single message in the chat history.
+ *
+ * @param chatId, the chat to get the messages from
+ * @param includeMe, include my own messages? boolean
+ * @param includeNotifications
+ * @returns Message[]
+ */
+ Client.prototype.getAllMessagesInChat = function (chatId, includeMe, includeNotifications) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, includeMe = _a.includeMe, includeNotifications = _a.includeNotifications;
+ return WAPI.getAllMessagesInChat(chatId, includeMe, includeNotifications);
+ }, { chatId: chatId, includeMe: includeMe, includeNotifications: includeNotifications })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * loads and Retrieves all Messages in a chat
+ * @param chatId, the chat to get the messages from
+ * @param includeMe, include my own messages? boolean
+ * @param includeNotifications
+ * @returns any
+ */
+ Client.prototype.loadAndGetAllMessagesInChat = function (chatId, includeMe, includeNotifications) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, includeMe = _a.includeMe, includeNotifications = _a.includeNotifications;
+ return WAPI.loadAndGetAllMessagesInChat(chatId, includeMe, includeNotifications);
+ }, { chatId: chatId, includeMe: includeMe, includeNotifications: includeNotifications })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Create a group and add contacts to it
+ *
+ * @param groupName group name: 'New group'
+ * @param contacts: A single contact id or an array of contact ids.
+ */
+ Client.prototype.createGroup = function (groupName, contacts) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupName = _a.groupName, contacts = _a.contacts;
+ return WAPI.createGroup(groupName, contacts);
+ }, { groupName: groupName, contacts: contacts })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Create a new community
+ *
+ * @param communityName The community name
+ * @param communitySubject: The community subject line
+ * @param icon DataURL of a 1:1 ratio jpeg for the community icon
+ * @param existingGroups An array of existing group IDs, that are not already part of a community, to add to this new community.
+ * @param newGroups An array of new group objects that
+ */
+ Client.prototype.createCommunity = function (communityName, communitySubject, icon, existingGroups, newGroups) {
+ if (existingGroups === void 0) { existingGroups = []; }
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var communityName = _a.communityName, communitySubject = _a.communitySubject, icon = _a.icon, existingGroups = _a.existingGroups, newGroups = _a.newGroups;
+ return WAPI.createCommunity(communityName, communitySubject, icon, existingGroups, newGroups);
+ }, { communityName: communityName, communitySubject: communitySubject, icon: icon, existingGroups: existingGroups, newGroups: newGroups })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Remove participant of Group
+ *
+ * If not a group chat, returns `NOT_A_GROUP_CHAT`.
+ *
+ * If the chat does not exist, returns `GROUP_DOES_NOT_EXIST`
+ *
+ * If the participantId does not exist in the group chat, returns `NOT_A_PARTICIPANT`
+ *
+ * If the host account is not an administrator, returns `INSUFFICIENT_PERMISSIONS`
+ *
+ * @param {*} groupId `0000000000-00000000@g.us`
+ * @param {*} participantId `000000000000@c.us`
+ */
+ Client.prototype.removeParticipant = function (groupId, participantId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, participantId = _a.participantId;
+ return WAPI.removeParticipant(groupId, participantId);
+ }, { groupId: groupId, participantId: participantId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /** Change the icon for the group chat
+ * @param groupId 123123123123_1312313123@g.us The id of the group
+ * @param imgData 'data:image/jpeg;base64,...` The base 64 data url. Make sure this is a small img (128x128), otherwise it will fail.
+ * @returns boolean true if it was set, false if it didn't work. It usually doesn't work if the image file is too big.
+ */
+ Client.prototype.setGroupIcon = function (groupId, image) {
+ return __awaiter(this, void 0, void 0, function () {
+ var mimeInfo, imgData;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ mimeInfo = (0, tools_1.base64MimeType)(image);
+ if (!(!mimeInfo || mimeInfo.includes("image"))) return [3 /*break*/, 3];
+ imgData = void 0;
+ return [4 /*yield*/, this.stickerServerRequest('convertGroupIcon', {
+ image: image
+ })];
+ case 1:
+ imgData = _a.sent();
+ return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, imgData = _a.imgData;
+ return WAPI.setGroupIcon(groupId, imgData);
+ }, { groupId: groupId, imgData: imgData })];
+ case 2: return [2 /*return*/, _a.sent()];
+ case 3: return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /** Change the icon for the group chat
+ * @param groupId 123123123123_1312313123@g.us The id of the group
+ * @param url'https://upload.wikimedia.org/wikipedia/commons/3/38/JPEG_example_JPG_RIP_001.jpg' The url of the image. Make sure this is a small img (128x128), otherwise it will fail.
+ * @param requestConfig {} By default the request is a get request, however you can override that and many other options by sending this parameter. You can read more about this parameter here: https://github.com/axios/axios#request-config
+ * @returns boolean true if it was set, false if it didn't work. It usually doesn't work if the image file is too big.
+ */
+ Client.prototype.setGroupIconByUrl = function (groupId, url, requestConfig) {
+ if (requestConfig === void 0) { requestConfig = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ var base64, error_5;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _a.trys.push([0, 3, , 4]);
+ return [4 /*yield*/, (0, tools_1.getDUrl)(url, requestConfig)];
+ case 1:
+ base64 = _a.sent();
+ return [4 /*yield*/, this.setGroupIcon(groupId, base64)];
+ case 2: return [2 /*return*/, _a.sent()];
+ case 3:
+ error_5 = _a.sent();
+ throw error_5;
+ case 4: return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /**
+ * Add participant to Group
+ *
+ * If not a group chat, returns `NOT_A_GROUP_CHAT`.
+ *
+ * If the chat does not exist, returns `GROUP_DOES_NOT_EXIST`
+ *
+ * If the participantId does not exist in the contacts, returns `NOT_A_CONTACT`
+ *
+ * If the host account is not an administrator, returns `INSUFFICIENT_PERMISSIONS`
+ *
+ * @param {*} groupId '0000000000-00000000@g.us'
+ * @param {*} participantId '000000000000@c.us'
+ *
+ */
+ Client.prototype.addParticipant = function (groupId, participantId) {
+ return __awaiter(this, void 0, void 0, function () {
+ var res;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, participantId = _a.participantId;
+ return WAPI.addParticipant(groupId, participantId);
+ }, { groupId: groupId, participantId: participantId })];
+ case 1:
+ res = _a.sent();
+ if (typeof res === "object")
+ throw new errors_1.AddParticipantError('Unable to add some participants', res);
+ if (typeof res === "string")
+ throw new errors_1.AddParticipantError(res);
+ return [2 /*return*/, res];
+ }
+ });
+ });
+ };
+ /**
+ * Promote Participant to Admin in Group
+ *
+ *
+ * If not a group chat, returns `NOT_A_GROUP_CHAT`.
+ *
+ * If the chat does not exist, returns `GROUP_DOES_NOT_EXIST`
+ *
+ * If the participantId does not exist in the group chat, returns `NOT_A_PARTICIPANT`
+ *
+ * If the host account is not an administrator, returns `INSUFFICIENT_PERMISSIONS`
+ *
+ * @param {*} groupId '0000000000-00000000@g.us'
+ * @param {*} participantId '000000000000@c.us'
+ */
+ Client.prototype.promoteParticipant = function (groupId, participantId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, participantId = _a.participantId;
+ return WAPI.promoteParticipant(groupId, participantId);
+ }, { groupId: groupId, participantId: participantId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Demote Admin of Group
+ *
+ * If not a group chat, returns `NOT_A_GROUP_CHAT`.
+ *
+ * If the chat does not exist, returns `GROUP_DOES_NOT_EXIST`
+ *
+ * If the participantId does not exist in the group chat, returns `NOT_A_PARTICIPANT`
+ *
+ * If the host account is not an administrator, returns `INSUFFICIENT_PERMISSIONS`
+ *
+ * @param {*} groupId '0000000000-00000000@g.us'
+ * @param {*} participantId '000000000000@c.us'
+ */
+ Client.prototype.demoteParticipant = function (groupId, participantId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, participantId = _a.participantId;
+ return WAPI.demoteParticipant(groupId, participantId);
+ }, { groupId: groupId, participantId: participantId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Change who can and cannot speak in a group
+ * @param groupId '0000000000-00000000@g.us' the group id.
+ * @param onlyAdmins boolean set to true if you want only admins to be able to speak in this group. false if you want to allow everyone to speak in the group
+ * @returns boolean true if action completed successfully.
+ */
+ Client.prototype.setGroupToAdminsOnly = function (groupId, onlyAdmins) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, onlyAdmins = _a.onlyAdmins;
+ return WAPI.setGroupToAdminsOnly(groupId, onlyAdmins);
+ }, { groupId: groupId, onlyAdmins: onlyAdmins })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Change who can and cannot edit a groups details
+ * @param groupId '0000000000-00000000@g.us' the group id.
+ * @param onlyAdmins boolean set to true if you want only admins to be able to speak in this group. false if you want to allow everyone to speak in the group
+ * @returns boolean true if action completed successfully.
+ */
+ Client.prototype.setGroupEditToAdminsOnly = function (groupId, onlyAdmins) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, onlyAdmins = _a.onlyAdmins;
+ return WAPI.setGroupEditToAdminsOnly(groupId, onlyAdmins);
+ }, { groupId: groupId, onlyAdmins: onlyAdmins })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Turn on or off the approval requirement for new members to join a group
+ * @param groupId '0000000000-00000000@g.us' the group id.
+ * @param requireApproval set to true to turn on the approval requirement, false to turn off
+ * @returns boolean true if action completed successfully.
+ */
+ Client.prototype.setGroupApprovalMode = function (groupId, requireApproval) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, requireApproval = _a.requireApproval;
+ return WAPI.setGroupApprovalMode(groupId, requireApproval);
+ }, { groupId: groupId, requireApproval: requireApproval })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Change the group chant description
+ * @param groupId '0000000000-00000000@g.us' the group id.
+ * @param description string The new group description
+ * @returns boolean true if action completed successfully.
+ */
+ Client.prototype.setGroupDescription = function (groupId, description) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, description = _a.description;
+ return WAPI.setGroupDescription(groupId, description);
+ }, { groupId: groupId, description: description })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Change the group chat title
+ * @param groupId '0000000000-00000000@g.us' the group id.
+ * @param title string The new group title
+ * @returns boolean true if action completed successfully.
+ */
+ Client.prototype.setGroupTitle = function (groupId, title) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var groupId = _a.groupId, title = _a.title;
+ return WAPI.setGroupTitle(groupId, title);
+ }, { groupId: groupId, title: title })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Get Admins of a Group
+ * @param {*} groupId '0000000000-00000000@g.us'
+ */
+ Client.prototype.getGroupAdmins = function (groupId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (groupId) { return WAPI.getGroupAdmins(groupId); }, groupId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Set the wallpaper background colour
+ * @param {string} hex '#FFF123'
+ */
+ Client.prototype.setChatBackgroundColourHex = function (hex) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (hex) { return WAPI.setChatBackgroundColourHex(hex); }, hex)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Join or leave the wa web beta program. Will return true of operation was successful.
+ *
+ * @param {boolean} join true to join the beta, false to leave
+ */
+ Client.prototype.joinWebBeta = function (join) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (join) { return WAPI.joinWebBeta(join); }, join)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Start dark mode [NOW GENERALLY AVAILABLE]
+ * @param {boolean} activate true to activate dark mode, false to deactivate
+ */
+ Client.prototype.darkMode = function (activate) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (activate) { return WAPI.darkMode(activate); }, activate)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Automatically reject calls on the host account device. Please note that the device that is calling you will continue to ring.
+ *
+ * Update: Due to the nature of MD, the host account will continue ringing.
+ *
+ * @param message optional message to send to the calling account when their call is detected and rejected
+ */
+ Client.prototype.autoReject = function (message) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (message) { return WAPI.autoReject(message); }, message)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns an array of contacts that have read the message. If the message does not exist, it will return an empty array. If the host account has disabled read receipts this may not work!
+ * Each of these contact objects have a property `t` which represents the time at which that contact read the message.
+ * @param messageId The message id
+ */
+ Client.prototype.getMessageReaders = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.getMessageReaders(messageId); }, messageId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Returns poll data including results and votes.
+ *
+ * @param messageId The message id of the Poll
+ */
+ Client.prototype.getPollData = function (messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (messageId) { return WAPI.getPollData(messageId); }, messageId)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Sends a sticker (including GIF) from a given URL
+ * @param to: The recipient id.
+ * @param url: The url of the image
+ * @param requestConfig {} By default the request is a get request, however you can override that and many other options by sending this parameter. You can read more about this parameter here: https://github.com/axios/axios#request-config
+ *
+ * @returns `Promise`
+ */
+ Client.prototype.sendStickerfromUrl = function (to, url, requestConfig, stickerMetadata) {
+ if (requestConfig === void 0) { requestConfig = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ var base64;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, (0, tools_1.getDUrl)(url, requestConfig)];
+ case 1:
+ base64 = _a.sent();
+ return [4 /*yield*/, this.sendImageAsSticker(to, base64, stickerMetadata)];
+ case 2: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Sends a sticker from a given URL
+ * @param to The recipient id.
+ * @param url The url of the image
+ * @param messageId The id of the message to reply to
+ * @param requestConfig {} By default the request is a get request, however you can override that and many other options by sending this parameter. You can read more about this parameter here: https://github.com/axios/axios#request-config
+ *
+ * @returns `Promise`
+ */
+ Client.prototype.sendStickerfromUrlAsReply = function (to, url, messageId, requestConfig, stickerMetadata) {
+ if (requestConfig === void 0) { requestConfig = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ var dUrl, processingResponse, webpBase64, metadata;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, (0, tools_1.getDUrl)(url, requestConfig)];
+ case 1:
+ dUrl = _a.sent();
+ return [4 /*yield*/, this.prepareWebp(dUrl, stickerMetadata)];
+ case 2:
+ processingResponse = _a.sent();
+ if (!processingResponse)
+ return [2 /*return*/, false];
+ webpBase64 = processingResponse.webpBase64, metadata = processingResponse.metadata;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var webpBase64 = _a.webpBase64, to = _a.to, metadata = _a.metadata, messageId = _a.messageId;
+ return WAPI.sendStickerAsReply(webpBase64, to, metadata, messageId);
+ }, { webpBase64: webpBase64, to: to, metadata: metadata, messageId: messageId })];
+ case 3: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * This function takes an image and sends it as a sticker to the recipient as a reply to another message.
+ *
+ *
+ * @param to The recipient id.
+ * @param image: [[DataURL]], [[Base64]], URL (string GET), Relative filepath (string), or Buffer of the image
+ * @param messageId The id of the message to reply to
+ * @param stickerMetadata Sticker metadata
+ */
+ Client.prototype.sendImageAsStickerAsReply = function (to, image, messageId, stickerMetadata) {
+ return __awaiter(this, void 0, void 0, function () {
+ var relativePath, processingResponse, webpBase64, metadata;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!((Buffer.isBuffer(image) || typeof image === 'object' || (image === null || image === void 0 ? void 0 : image.type) === 'Buffer') && image.toString)) return [3 /*break*/, 1];
+ image = image.toString('base64');
+ return [3 /*break*/, 6];
+ case 1:
+ if (!(typeof image === 'string')) return [3 /*break*/, 6];
+ if (!(!(0, tools_1.isDataURL)(image) && !(0, tools_1.isBase64)(image))) return [3 /*break*/, 6];
+ if (!(0, is_url_superb_1["default"])(image)) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, tools_1.getDUrl)(image)];
+ case 2:
+ image = _a.sent();
+ return [3 /*break*/, 6];
+ case 3:
+ relativePath = path.join(path.resolve(process.cwd(), image || ''));
+ if (!(fs.existsSync(image) || fs.existsSync(relativePath))) return [3 /*break*/, 5];
+ return [4 /*yield*/, (0, datauri_1["default"])(fs.existsSync(image) ? image : relativePath)];
+ case 4:
+ image = _a.sent();
+ return [3 /*break*/, 6];
+ case 5: return [2 /*return*/, 'FILE_NOT_FOUND'];
+ case 6: return [4 /*yield*/, this.prepareWebp(image, stickerMetadata)];
+ case 7:
+ processingResponse = _a.sent();
+ if (!processingResponse)
+ return [2 /*return*/, false];
+ webpBase64 = processingResponse.webpBase64, metadata = processingResponse.metadata;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var webpBase64 = _a.webpBase64, to = _a.to, metadata = _a.metadata, messageId = _a.messageId;
+ return WAPI.sendStickerAsReply(webpBase64, to, metadata, messageId);
+ }, { webpBase64: webpBase64, to: to, metadata: metadata, messageId: messageId })];
+ case 8: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * This allows you to get a single property of a single object from the session. This limints the amouunt of data you need to sift through, reduces congestion between your process and the session and the flexibility to build your own specific getters.
+ *
+ * Example - get message read state (ack):
+ *
+ * ```javascript
+ * const ack = await client.getSingleProperty('Msg',"true_12345678912@c.us_9C4D0965EA5C09D591334AB6BDB07FEB",'ack')
+ * ```
+ * @param namespace
+ * @param id id of the object to get from the specific namespace
+ * @param property the single property key to get from the object.
+ * @returns any If the property or the id cannot be found, it will return a 404
+ */
+ Client.prototype.getSingleProperty = function (namespace, id, property) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var namespace = _a.namespace, id = _a.id, property = _a.property;
+ return WAPI.getSingleProperty(namespace, id, property);
+ }, { namespace: namespace, id: id, property: property })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ Client.prototype.stickerServerRequest = function (func, a, fallback) {
+ var _a, _b, _c, _d;
+ if (a === void 0) { a = {}; }
+ if (fallback === void 0) { fallback = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ var stickerUrl, sessionInfo, key, relativePath, _e, _f, url, data, err_1;
+ return __generator(this, function (_g) {
+ switch (_g.label) {
+ case 0:
+ stickerUrl = this._createConfig.stickerServerEndpoint || (fallback ? pkg.stickerUrl : "https://sticker-api.openwa.dev") || "https://sticker-api.openwa.dev";
+ if (func === 'convertMp4BufferToWebpDataUrl')
+ fallback = true;
+ sessionInfo = this.getSessionInfo();
+ sessionInfo.WA_AUTOMATE_VERSION = sessionInfo.WA_AUTOMATE_VERSION.split(' ')[0];
+ if (!(a.file || a.image || a.emojiId)) return [3 /*break*/, 12];
+ if (!!a.emojiId) return [3 /*break*/, 4];
+ key = a.file ? 'file' : 'image';
+ if (!(!(0, tools_1.isDataURL)(a[key]) && !(0, is_url_superb_1["default"])(a[key]) && !(0, tools_1.isBase64)(a[key]))) return [3 /*break*/, 3];
+ relativePath = path.join(path.resolve(process.cwd(), a[key] || ''));
+ if (!(fs.existsSync(a[key]) || fs.existsSync(relativePath))) return [3 /*break*/, 2];
+ _e = a;
+ _f = key;
+ return [4 /*yield*/, (0, datauri_1["default"])(fs.existsSync(a[key]) ? a[key] : relativePath)];
+ case 1:
+ _e[_f] = _g.sent();
+ return [3 /*break*/, 3];
+ case 2:
+ console.error('FILE_NOT_FOUND');
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.FILE_NOT_FOUND, 'FILE NOT FOUND');
+ case 3:
+ if ((a === null || a === void 0 ? void 0 : a.stickerMetadata) && typeof (a === null || a === void 0 ? void 0 : a.stickerMetadata) !== "object")
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.BAD_STICKER_METADATA, "Received ".concat(typeof (a === null || a === void 0 ? void 0 : a.stickerMetadata), ": ").concat(a === null || a === void 0 ? void 0 : a.stickerMetadata));
+ _g.label = 4;
+ case 4:
+ if ((_a = this._createConfig) === null || _a === void 0 ? void 0 : _a.discord) {
+ a.stickerMetadata = __assign(__assign({}, (a.stickerMetadata || {})), { discord: "".concat(((_b = a.stickerMetadata) === null || _b === void 0 ? void 0 : _b.discord) || this._createConfig.discord) });
+ }
+ _g.label = 5;
+ case 5:
+ _g.trys.push([5, 7, , 11]);
+ url = "".concat(stickerUrl.replace(/\/$/, ''), "/").concat(func);
+ logging_1.log.info("Requesting sticker from ".concat(url));
+ return [4 /*yield*/, axios_1["default"].post(url, __assign(__assign({}, a), { sessionInfo: sessionInfo, config: this.getConfig() }), {
+ maxBodyLength: 20000000,
+ maxContentLength: 1500000 // 1.5mb response body limit
+ })];
+ case 6:
+ data = (_g.sent()).data;
+ return [2 /*return*/, data];
+ case 7:
+ err_1 = _g.sent();
+ if (!(err_1 === null || err_1 === void 0 ? void 0 : err_1.message.includes("maxContentLength size"))) return [3 /*break*/, 8];
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.STICKER_TOO_LARGE, err_1 === null || err_1 === void 0 ? void 0 : err_1.message);
+ case 8:
+ if (!!fallback) return [3 /*break*/, 10];
+ return [4 /*yield*/, this.stickerServerRequest(func, a, true)];
+ case 9: return [2 /*return*/, _g.sent()];
+ case 10:
+ console.error((_c = err_1 === null || err_1 === void 0 ? void 0 : err_1.response) === null || _c === void 0 ? void 0 : _c.status, (_d = err_1 === null || err_1 === void 0 ? void 0 : err_1.response) === null || _d === void 0 ? void 0 : _d.data);
+ throw err_1;
+ case 11: return [3 /*break*/, 13];
+ case 12:
+ console.error("Media is missing from this request");
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.MEDIA_MISSING, "Media is missing from this request");
+ case 13: return [2 /*return*/];
+ }
+ });
+ });
+ };
+ Client.prototype.prepareWebp = function (image, stickerMetadata) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if ((0, tools_1.isDataURL)(image) && !image.includes("image")) {
+ console.error("Not an image. Please use convertMp4BufferToWebpDataUrl to process video stickers");
+ return [2 /*return*/, false];
+ }
+ return [4 /*yield*/, this.stickerServerRequest('prepareWebp', {
+ image: image,
+ stickerMetadata: stickerMetadata
+ })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * This function takes an image (including animated GIF) and sends it as a sticker to the recipient. This is helpful for sending semi-ephemeral things like QR codes.
+ * The advantage is that it will not show up in the recipients gallery. This function automatiicaly converts images to the required webp format.
+ * @param to: The recipient id.
+ * @param image: [[DataURL]], [[Base64]], URL (string GET), Relative filepath (string), or Buffer of the image
+ */
+ Client.prototype.sendImageAsSticker = function (to, image, stickerMetadata) {
+ return __awaiter(this, void 0, void 0, function () {
+ var relativePath, processingResponse, webpBase64, metadata;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!((Buffer.isBuffer(image) || typeof image === 'object' || (image === null || image === void 0 ? void 0 : image.type) === 'Buffer') && image.toString)) return [3 /*break*/, 1];
+ image = image.toString('base64');
+ return [3 /*break*/, 6];
+ case 1:
+ if (!(typeof image === 'string')) return [3 /*break*/, 6];
+ if (!(!(0, tools_1.isDataURL)(image) && !(0, tools_1.isBase64)(image))) return [3 /*break*/, 6];
+ if (!(0, is_url_superb_1["default"])(image)) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, tools_1.getDUrl)(image)];
+ case 2:
+ image = _a.sent();
+ return [3 /*break*/, 6];
+ case 3:
+ relativePath = path.join(path.resolve(process.cwd(), image || ''));
+ if (!(fs.existsSync(image) || fs.existsSync(relativePath))) return [3 /*break*/, 5];
+ return [4 /*yield*/, (0, datauri_1["default"])(fs.existsSync(image) ? image : relativePath)];
+ case 4:
+ image = _a.sent();
+ return [3 /*break*/, 6];
+ case 5: return [2 /*return*/, 'FILE_NOT_FOUND'];
+ case 6: return [4 /*yield*/, this.prepareWebp(image, stickerMetadata)];
+ case 7:
+ processingResponse = _a.sent();
+ if (!processingResponse)
+ return [2 /*return*/, false];
+ webpBase64 = processingResponse.webpBase64, metadata = processingResponse.metadata;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var webpBase64 = _a.webpBase64, to = _a.to, metadata = _a.metadata;
+ return WAPI.sendImageAsSticker(webpBase64, to, metadata);
+ }, { webpBase64: webpBase64, to: to, metadata: metadata })];
+ case 8: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Use this to send an mp4 file as a sticker. This can also be used to convert GIFs from the chat because GIFs in WA are actually tiny mp4 files.
+ *
+ * @param to ChatId The chat id you want to send the webp sticker to
+ * @param file [[DataURL]], [[Base64]], URL (string GET), Relative filepath (string), or Buffer of the mp4 file
+ * @param messageId message id of the message you want this sticker to reply to. @license:insiders@
+ */
+ Client.prototype.sendMp4AsSticker = function (to, file, processOptions, stickerMetadata, messageId) {
+ if (processOptions === void 0) { processOptions = media_1.defaultProcessOptions; }
+ return __awaiter(this, void 0, void 0, function () {
+ var relativePath, convertedStickerDataUrl, error_6, msg;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ //@ts-ignore
+ if ((Buffer.isBuffer(file) || typeof file === 'object' || (file === null || file === void 0 ? void 0 : file.type) === 'Buffer') && file.toString) {
+ file = file.toString('base64');
+ }
+ if (!(typeof file === 'string')) return [3 /*break*/, 5];
+ if (!(!(0, tools_1.isDataURL)(file) && !(0, tools_1.isBase64)(file))) return [3 /*break*/, 5];
+ if (!(0, is_url_superb_1["default"])(file)) return [3 /*break*/, 2];
+ return [4 /*yield*/, (0, tools_1.getDUrl)(file)];
+ case 1:
+ file = _a.sent();
+ return [3 /*break*/, 5];
+ case 2:
+ relativePath = path.join(path.resolve(process.cwd(), file || ''));
+ if (!(fs.existsSync(file) || fs.existsSync(relativePath))) return [3 /*break*/, 4];
+ return [4 /*yield*/, (0, datauri_1["default"])(fs.existsSync(file) ? file : relativePath)];
+ case 3:
+ file = _a.sent();
+ return [3 /*break*/, 5];
+ case 4: return [2 /*return*/, 'FILE_NOT_FOUND'];
+ case 5: return [4 /*yield*/, this.stickerServerRequest('convertMp4BufferToWebpDataUrl', {
+ file: file,
+ processOptions: processOptions,
+ stickerMetadata: stickerMetadata
+ })];
+ case 6:
+ convertedStickerDataUrl = _a.sent();
+ _a.label = 7;
+ case 7:
+ _a.trys.push([7, 9, , 10]);
+ if (!convertedStickerDataUrl)
+ return [2 /*return*/, false];
+ return [4 /*yield*/, (messageId && this._createConfig.licenseKey)];
+ case 8: return [2 /*return*/, (_a.sent()) ? this.sendRawWebpAsStickerAsReply(to, messageId, convertedStickerDataUrl, true) : this.sendRawWebpAsSticker(to, convertedStickerDataUrl, true)];
+ case 9:
+ error_6 = _a.sent();
+ msg = 'Stickers have to be less than 1MB. Please lower the fps or shorten the duration using the processOptions parameter: https://open-wa.github.io/wa-automate-nodejs/classes/client.html#sendmp4assticker';
+ console.log(msg);
+ logging_1.log.warn(msg);
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.STICKER_TOO_LARGE, msg);
+ case 10: return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /**
+ * Send a discord emoji to a chat as a sticker
+ *
+ * @param to ChatId The chat id you want to send the webp sticker to
+ * @param emojiId The discord emoji id without indentifying chars. In discord you would write `:who:`, here use `who`
+ * @param messageId message id of the message you want this sticker to reply to. @license:insiders@
+ */
+ Client.prototype.sendEmoji = function (to, emojiId, messageId) {
+ return __awaiter(this, void 0, void 0, function () {
+ var webp;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.stickerServerRequest('emoji', {
+ emojiId: emojiId
+ })];
+ case 1:
+ webp = _a.sent();
+ if (!webp) return [3 /*break*/, 5];
+ if (!messageId) return [3 /*break*/, 3];
+ return [4 /*yield*/, this.sendRawWebpAsStickerAsReply(to, messageId, webp, true)];
+ case 2: return [2 /*return*/, _a.sent()];
+ case 3: return [4 /*yield*/, this.sendRawWebpAsSticker(to, webp, true)];
+ case 4: return [2 /*return*/, _a.sent()];
+ case 5: return [2 /*return*/, false];
+ }
+ });
+ });
+ };
+ /**
+ * You can use this to send a raw webp file.
+ * @param to ChatId The chat id you want to send the webp sticker to
+ * @param webpBase64 Base64 The base64 string of the webp file. Not DataURl
+ * @param animated Boolean Set to true if the webp is animated. Default `false`
+ */
+ Client.prototype.sendRawWebpAsSticker = function (to, webpBase64, animated) {
+ if (animated === void 0) { animated = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ var metadata;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ metadata = {
+ format: 'webp',
+ width: 512,
+ height: 512,
+ animated: animated
+ };
+ webpBase64 = webpBase64.replace(/^data:image\/(png|gif|jpeg|webp|octet-stream);base64,/, '');
+ return [4 /*yield*/, this.pup(function (_a) {
+ var webpBase64 = _a.webpBase64, to = _a.to, metadata = _a.metadata;
+ return WAPI.sendImageAsSticker(webpBase64, to, metadata);
+ }, { webpBase64: webpBase64, to: to, metadata: metadata })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * You can use this to send a raw webp file.
+ * @param to ChatId The chat id you want to send the webp sticker to
+ * @param messageId MessageId Message ID of the message to reply to
+ * @param webpBase64 Base64 The base64 string of the webp file. Not DataURl
+ * @param animated Boolean Set to true if the webp is animated. Default `false`
+ */
+ Client.prototype.sendRawWebpAsStickerAsReply = function (to, messageId, webpBase64, animated) {
+ if (animated === void 0) { animated = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ var metadata;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ metadata = {
+ format: 'webp',
+ width: 512,
+ height: 512,
+ animated: animated
+ };
+ webpBase64 = webpBase64.replace(/^data:image\/(png|gif|jpeg|webp);base64,/, '');
+ return [4 /*yield*/, this.pup(function (_a) {
+ var webpBase64 = _a.webpBase64, to = _a.to, metadata = _a.metadata, messageId = _a.messageId;
+ return WAPI.sendStickerAsReply(webpBase64, to, metadata, messageId);
+ }, { webpBase64: webpBase64, to: to, metadata: metadata, messageId: messageId })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:insiders@}
+ *
+ * Turn the ephemeral setting in a chat to on or off
+ * @param chatId The ID of the chat
+ * @param ephemeral `true` to turn on the ephemeral setting to 1 day, `false` to turn off the ephemeral setting. Other options: `604800 | 7776000`
+ * @returns `Promise` true if the setting was set, `false` if the chat does not exist
+ */
+ Client.prototype.setChatEphemeral = function (chatId, ephemeral) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var chatId = _a.chatId, ephemeral = _a.ephemeral;
+ return WAPI.setChatEphemeral(chatId, ephemeral);
+ }, { chatId: chatId, ephemeral: ephemeral })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Send a giphy GIF as an animated sticker.
+ * @param to ChatId
+ * @param giphyMediaUrl URL | string This is the giphy media url and has to be in the format `https://media.giphy.com/media/RJKHjCAdsAfQPn03qQ/source.gif` or it can be just the id `RJKHjCAdsAfQPn03qQ`
+ */
+ Client.prototype.sendGiphyAsSticker = function (to, giphyMediaUrl) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var to = _a.to, giphyMediaUrl = _a.giphyMediaUrl;
+ return WAPI.sendGiphyAsSticker(to, giphyMediaUrl);
+ }, { to: to, giphyMediaUrl: giphyMediaUrl })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Sends a formatted text story.
+ * @param text The text to be displayed in the story
+ * @param textRgba The colour of the text in the story in hex format, make sure to add the alpha value also. E.g "#FF00F4F2"
+ * @param backgroundRgba The colour of the background in the story in hex format, make sure to add the alpha value also. E.g "#4FF31FF2"
+ * @param font The font of the text to be used in the story. This has to be a number. Each number refers to a specific predetermined font. Here are the fonts you can choose from:
+ * 0: Sans Serif
+ * 1: Serif
+ * 2: [Norican Regular](https://fonts.google.com/specimen/Norican)
+ * 3: [Bryndan Write](https://www.dafontfree.net/freefonts-bryndan-write-f160189.htm)
+ * 4: [Bebasneue Regular](https://www.dafont.com/bebas-neue.font)
+ * 5: [Oswald Heavy](https://www.fontsquirrel.com/fonts/oswald)
+ * @returns `Promise` returns status id if it worked, false if it didn't
+ */
+ Client.prototype.postTextStatus = function (text, textRgba, backgroundRgba, font) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var text = _a.text, textRgba = _a.textRgba, backgroundRgba = _a.backgroundRgba, font = _a.font;
+ return WAPI.postTextStatus(text, textRgba, backgroundRgba, font);
+ }, { text: text, textRgba: textRgba, backgroundRgba: backgroundRgba, font: font })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Sends a formatted text story with a thumbnail.
+ * @param url The URL to share in the story
+ * @param text The text to be displayed in the story
+ * @param textRgba The colour of the text in the story in hex format, make sure to add the alpha value also. E.g "#FF00F4F2"
+ * @param backgroundRgba The colour of the background in the story in hex format, make sure to add the alpha value also. E.g "#4FF31FF2"
+ * @param font The font of the text to be used in the story. This has to be a number. Each number refers to a specific predetermined font. Here are the fonts you can choose from:
+ * @param thumbnail base64 thumbnail override, if not provided the link server will try to figure it out.
+ * 0: Sans Serif
+ * 1: Serif
+ * 2: [Norican Regular](https://fonts.google.com/specimen/Norican)
+ * 3: [Bryndan Write](https://www.dafontfree.net/freefonts-bryndan-write-f160189.htm)
+ * @returns `Promise` returns status id if it worked, false if it didn't
+ */
+ Client.prototype.postThumbnailStatus = function (url, text, textRgba, backgroundRgba, font, thumbnail) {
+ var _a;
+ return __awaiter(this, void 0, void 0, function () {
+ var linkData, thumb, error_7, title, description;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ thumb = thumbnail;
+ _b.label = 1;
+ case 1:
+ _b.trys.push([1, 5, , 6]);
+ return [4 /*yield*/, axios_1["default"].get("".concat(((_a = this._createConfig) === null || _a === void 0 ? void 0 : _a.linkParser) || "https://link.openwa.cloud/api", "?url=").concat(url))];
+ case 2:
+ linkData = (_b.sent()).data;
+ logging_1.log.info("Got link data");
+ if (!!thumbnail) return [3 /*break*/, 4];
+ return [4 /*yield*/, (0, tools_1.getDUrl)(linkData.image)];
+ case 3:
+ thumb = _b.sent();
+ _b.label = 4;
+ case 4: return [3 /*break*/, 6];
+ case 5:
+ error_7 = _b.sent();
+ console.error(error_7);
+ return [3 /*break*/, 6];
+ case 6:
+ title = linkData.title, description = linkData.description;
+ return [4 /*yield*/, this.pup(function (_a) {
+ var thumb = _a.thumb, url = _a.url, title = _a.title, description = _a.description, text = _a.text, textRgba = _a.textRgba, backgroundRgba = _a.backgroundRgba, font = _a.font;
+ return WAPI.sendStoryWithThumb(thumb, url, title, description, text, textRgba, backgroundRgba, font);
+ }, { thumb: thumb, url: url, title: title, description: description, text: text, textRgba: textRgba, backgroundRgba: backgroundRgba, font: font })];
+ case 7: return [2 /*return*/, _b.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Posts an image story.
+ * @param data data url string `data:[][;charset=][;base64],`
+ * @param caption The caption for the story
+ * @returns `Promise` returns status id if it worked, false if it didn't
+ */
+ Client.prototype.postImageStatus = function (data, caption) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var data = _a.data, caption = _a.caption;
+ return WAPI.postImageStatus(data, caption);
+ }, { data: data, caption: caption })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Posts a video story.
+ * @param data data url string `data:[][;charset=][;base64],`
+ * @param caption The caption for the story
+ * @returns `Promise` returns status id if it worked, false if it didn't
+ */
+ Client.prototype.postVideoStatus = function (data, caption) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var data = _a.data, caption = _a.caption;
+ return WAPI.postVideoStatus(data, caption);
+ }, { data: data, caption: caption })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Consumes a list of id strings of stories to delete.
+ *
+ * @param statusesToDelete string [] | string an array of ids of stories to delete.
+ * @returns boolean. True if it worked.
+ */
+ Client.prototype.deleteStory = function (statusesToDelete) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var statusesToDelete = _a.statusesToDelete;
+ return WAPI.deleteStatus(statusesToDelete);
+ }, { statusesToDelete: statusesToDelete })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Alias for deleteStory
+ */
+ Client.prototype.deleteStatus = function (statusesToDelete) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.deleteStory(statusesToDelete)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Deletes all your existing stories.
+ * @returns boolean. True if it worked.
+ */
+ Client.prototype.deleteAllStories = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.deleteAllStatus(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Alias for deleteStory
+ */
+ Client.prototype.deleteAllStatus = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.deleteAllStories()];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Retrieves all existing stories.
+ *
+ * Only works with a Story License Key
+ */
+ Client.prototype.getMyStoryArray = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.getMyStatusArray(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Alias for deleteStory
+ */
+ Client.prototype.getMyStatusArray = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.getMyStoryArray()];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * {@license:restricted@}
+ *
+ * Retrieves an array of user ids that have 'read' your story.
+ *
+ * @param id string The id of the story
+ *
+ */
+ Client.prototype.getStoryViewers = function (id) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var id = _a.id;
+ return WAPI.getStoryViewers(id);
+ }, { id: id })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Clears all chats of all messages. This does not delete chats. Please be careful with this as it will remove all messages from whatsapp web and the host device. This feature is great for privacy focussed bots.
+ *
+ * @param ts number A chat that has had a message after ts (epoch timestamp) will not be cleared.
+ *
+ */
+ Client.prototype.clearAllChats = function (ts) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var ts = _a.ts;
+ return WAPI.clearAllChats(ts);
+ }, { ts: ts })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * This simple function halves the amount of messages in your session message cache. This does not delete messages off your phone. If over a day you've processed 4000 messages this will possibly result in 4000 messages being present in your session.
+ * Calling this method will cut the message cache to 2000 messages, therefore reducing the memory usage of your process.
+ * You should use this in conjunction with `getAmountOfLoadedMessages` to intelligently control the session message cache.
+ */
+ Client.prototype.cutMsgCache = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.cutMsgCache(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * This simple function halves the amount of chats in your session message cache. This does not delete messages off your phone. If over a day you've processed 4000 messages this will possibly result in 4000 messages being present in your session.
+ * Calling this method will cut the message cache as much as possible, reducing the memory usage of your process.
+ * You should use this in conjunction with `getAmountOfLoadedMessages` to intelligently control the session message cache.
+ */
+ Client.prototype.cutChatCache = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function () { return WAPI.cutChatCache(); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Deletes chats from a certain index (default 1000). E.g if this startingFrom param is `100` then all chats from index `100` onwards will be deleted.
+ *
+ * @param startingFrom the chat index to start from. Please do not set this to anything less than 10 @default: `1000`
+ */
+ Client.prototype.deleteStaleChats = function (startingFrom) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var startingFrom = _a.startingFrom;
+ return WAPI.deleteStaleChats(startingFrom);
+ }, { startingFrom: startingFrom })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Download profile pics from the message object.
+ * ```javascript
+ * const filename = `profilepic_${message.from}.jpeg`;
+ * const data = await client.downloadProfilePicFromMessage(message);
+ * const dataUri = `data:image/jpeg;base64,${data}`;
+ * fs.writeFile(filename, mData, 'base64', function(err) {
+ * if (err) {
+ * return console.log(err);
+ * }
+ * console.log('The file was saved!');
+ * });
+ * ```
+ */
+ Client.prototype.downloadProfilePicFromMessage = function (message) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.downloadFileWithCredentials(message.sender.profilePicThumbObj.imgFull)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Download via the browsers authenticated session via URL.
+ * @returns base64 string (non-data url)
+ */
+ Client.prototype.downloadFileWithCredentials = function (url) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!url)
+ throw new errors_1.CustomError(errors_1.ERROR_NAME.MISSING_URL, 'Missing URL');
+ return [4 /*yield*/, this.pup(function (_a) {
+ var url = _a.url;
+ return WAPI.downloadFileWithCredentials(url);
+ }, { url: url })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ *
+ * Sets the profile pic of the host number.
+ * @param data string data url image string.
+ * @returns `Promise` success if true
+ */
+ Client.prototype.setProfilePic = function (data) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this.pup(function (_a) {
+ var data = _a.data;
+ return WAPI.setProfilePic(data);
+ }, { data: data })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ /**
+ * Retreives an array of webhook objects
+ */
+ Client.prototype.listWebhooks = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ var _this = this;
+ return __generator(this, function (_a) {
+ return [2 /*return*/, this._registeredWebhooks ? Object.keys(this._registeredWebhooks).map(function (id) { return _this._registeredWebhooks[id]; }).map(function (_a) {
+ var
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ requestConfig = _a.requestConfig, rest = __rest(_a, ["requestConfig"]);
+ return rest;
+ }) : []];
+ });
+ });
+ };
+ /**
+ * Removes a webhook.
+ *
+ * Returns `true` if the webhook was found and removed. `false` if the webhook was not found and therefore could not be removed. This does not unregister any listeners off of other webhooks.
+ *
+ *
+ * @param webhookId The ID of the webhook
+ * @retruns boolean
+ */
+ Client.prototype.removeWebhook = function (webhookId) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ if (this._registeredWebhooks[webhookId]) {
+ delete this._registeredWebhooks[webhookId];
+ return [2 /*return*/, true]; //`Webhook for ${simpleListener} removed`
+ }
+ return [2 /*return*/, false]; //`Webhook for ${simpleListener} not found`
+ });
+ });
+ };
+ /**
+ * Update registered events for a specific webhook. This will override all existing events. If you'd like to remove all listeners from a webhook, consider using [[removeWebhook]].
+ *
+ * In order to update authentication details for a webhook, remove it completely and then reregister it with the correct credentials.
+ */
+ Client.prototype.updateWebhook = function (webhookId, events) {
+ return __awaiter(this, void 0, void 0, function () {
+ var validListeners, _a,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ requestConfig, rest;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (events === "all")
+ events = Object.keys(events_2.SimpleListener).map(function (eventKey) { return events_2.SimpleListener[eventKey]; });
+ if (!Array.isArray(events))
+ events = [events];
+ return [4 /*yield*/, this._setupWebhooksOnListeners(events)];
+ case 1:
+ validListeners = _b.sent();
+ if (this._registeredWebhooks[webhookId]) {
+ this._registeredWebhooks[webhookId].events = validListeners;
+ _a = this._registeredWebhooks[webhookId], requestConfig = _a.requestConfig, rest = __rest(_a, ["requestConfig"]);
+ return [2 /*return*/, rest];
+ }
+ return [2 /*return*/, false];
+ }
+ });
+ });
+ };
+ /**
+ * The client can now automatically handle webhooks. Use this method to register webhooks.
+ *
+ * @param event use [[SimpleListener]] enum
+ * @param url The webhook url
+ * @param requestConfig {} By default the request is a post request, however you can override that and many other options by sending this parameter. You can read more about this parameter here: https://github.com/axios/axios#request-config
+ * @param concurrency the amount of concurrent requests to be handled by the built in queue. Default is 5.
+ */
+ // public async registerWebhook(event: SimpleListener, url: string, requestConfig: AxiosRequestConfig = {}, concurrency: number = 5) {
+ // if(!this._webhookQueue) this._webhookQueue = new PQueue({ concurrency });
+ // if(this[event]){
+ // if(!this._registeredWebhooks) this._registeredWebhooks={};
+ // if(this._registeredWebhooks[event]) {
+ // console.log('webhook already registered');
+ // return false;
+ // }
+ // this._registeredWebhooks[event] = this[event](async _data=>await this._webhookQueue.add(async () => await axios({
+ // method: 'post',
+ // url,
+ // data: {
+ // ts: Date.now(),
+ // event,
+ // data:_data
+ // },
+ // ...requestConfig
+ // })));
+ // return this._registeredWebhooks[event];
+ // }
+ // console.log('Invalid lisetner', event);
+ // return false;
+ // }
+ Client.prototype._setupWebhooksOnListeners = function (events) {
+ return __awaiter(this, void 0, void 0, function () {
+ var validListeners;
+ var _this = this;
+ return __generator(this, function (_a) {
+ if (events === "all")
+ events = Object.keys(events_2.SimpleListener).map(function (eventKey) { return events_2.SimpleListener[eventKey]; });
+ if (!Array.isArray(events))
+ events = [events];
+ if (!this._registeredWebhookListeners)
+ this._registeredWebhookListeners = {};
+ if (!this._registeredWebhooks)
+ this._registeredWebhooks = {};
+ validListeners = [];
+ events.map(function (event) {
+ if (!event.startsWith("on"))
+ event = "on".concat(event);
+ if (_this[event]) {
+ validListeners.push(event);
+ if (_this._registeredWebhookListeners[event] === undefined) {
+ //set it up
+ _this._registeredWebhookListeners[event] = _this[event](function (_data) { return __awaiter(_this, void 0, void 0, function () {
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, this._webhookQueue.add(function () { return __awaiter(_this, void 0, void 0, function () {
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, Promise.all(__spreadArray([], Object.keys(this._registeredWebhooks).map(function (webhookId) { return _this._registeredWebhooks[webhookId]; }).filter(function (webhookEntry) { return webhookEntry.events.includes(event); }), true).map(function (_a) {
+ var id = _a.id, url = _a.url, requestConfig = _a.requestConfig;
+ var whStart = (0, tools_1.now)();
+ return (0, axios_1["default"])(__assign({ method: 'post', url: url, data: _this.prepEventData(_data, event, { webhook_id: id }) }, requestConfig))
+ .then(function (_a) {
+ var status = _a.status;
+ var t = ((0, tools_1.now)() - whStart).toFixed(0);
+ logging_1.log.info("Client Webhook", event, status, t);
+ })["catch"](function (err) { return logging_1.log.error("CLIENT WEBHOOK ERROR: ", url, err.message); });
+ }))];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ }); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ }); }, 10000);
+ }
+ }
+ });
+ return [2 /*return*/, validListeners];
+ });
+ });
+ };
+ /**
+ * The client can now automatically handle webhooks. Use this method to register webhooks.
+ *
+ * @param url The webhook url
+ * @param events An array of [[SimpleListener]] enums or `all` (to register all possible listeners)
+ * @param requestConfig {} By default the request is a post request, however you can override that and many other options by sending this parameter. You can read more about this parameter here: https://github.com/axios/axios#request-config
+ * @param concurrency the amount of concurrent requests to be handled by the built in queue. Default is 5.
+ * @returns A webhook object. This will include a webhook ID and an array of all successfully registered Listeners.
+ */
+ Client.prototype.registerWebhook = function (url, events, requestConfig, concurrency) {
+ if (requestConfig === void 0) { requestConfig = {}; }
+ if (concurrency === void 0) { concurrency = 5; }
+ return __awaiter(this, void 0, void 0, function () {
+ var validListeners, id;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!this._webhookQueue)
+ this._webhookQueue = new p_queue_1["default"]({ concurrency: concurrency });
+ return [4 /*yield*/, this._setupWebhooksOnListeners(events)];
+ case 1:
+ validListeners = _a.sent();
+ id = (0, uuid_1.v4)();
+ if (validListeners.length) {
+ this._registeredWebhooks[id] = {
+ id: id,
+ ts: Date.now(),
+ url: url,
+ events: validListeners,
+ requestConfig: requestConfig
+ };
+ return [2 /*return*/, this._registeredWebhooks[id]];
+ }
+ console.log('Invalid listener(s)', events);
+ logging_1.log.warn('Invalid listener(s)', events);
+ return [2 /*return*/, false];
+ }
+ });
+ });
+ };
+ Client.prototype.prepEventData = function (data, event, extras) {
+ var sessionId = this.getSessionId();
+ return __assign({ ts: Date.now(), sessionId: sessionId, id: (0, uuid_1.v4)(), event: event, data: data }, extras);
+ };
+ Client.prototype.getEventSignature = function (simpleListener) {
+ return "".concat(simpleListener || '**', ".").concat(this._createConfig.sessionId || 'session', ".").concat(this._sessionInfo.INSTANCE_ID);
+ };
+ Client.prototype.registerEv = function (simpleListener) {
+ return __awaiter(this, void 0, void 0, function () {
+ var _a, _b;
+ var _this = this;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ if (!this[simpleListener]) return [3 /*break*/, 2];
+ if (!this._registeredEvListeners)
+ this._registeredEvListeners = {};
+ if (this._registeredEvListeners[simpleListener]) {
+ console.log('Listener already registered');
+ logging_1.log.warn('Listener already registered');
+ return [2 /*return*/, false];
+ }
+ _a = this._registeredEvListeners;
+ _b = simpleListener;
+ return [4 /*yield*/, this[simpleListener](function (data) { return events_1.ev.emit(_this.getEventSignature(simpleListener), _this.prepEventData(data, simpleListener)); })];
+ case 1:
+ _a[_b] = _c.sent();
+ return [2 /*return*/, true];
+ case 2:
+ console.log('Invalid lisetner', simpleListener);
+ logging_1.log.warn('Invalid lisetner', simpleListener);
+ return [2 /*return*/, false];
+ }
+ });
+ });
+ };
+ /**
+ * Every time this is called, it returns one less number. This is used to sort out queue priority.
+ */
+ Client.prototype.tickPriority = function () {
+ this._prio = this._prio - 1;
+ return this._prio;
+ };
+ /**
+ * Get the INSTANCE_ID of the current session
+ */
+ Client.prototype.getInstanceId = function () {
+ return this._sessionInfo.INSTANCE_ID;
+ };
+ /**
+ * Returns a new message collector for the chat which is related to the first parameter c
+ * @param c The Mesasge/Chat or Chat Id to base this message colletor on
+ * @param filter A function that consumes a [Message] and returns a boolean which determines whether or not the message shall be collected.
+ * @param options The options for the collector. For example, how long the collector shall run for, how many messages it should collect, how long between messages before timing out, etc.
+ */
+ Client.prototype.createMessageCollector = function (c, filter, options) {
+ var _a;
+ var chatId = (((_a = c === null || c === void 0 ? void 0 : c.chat) === null || _a === void 0 ? void 0 : _a.id) || (c === null || c === void 0 ? void 0 : c.id) || c);
+ return new MessageCollector_1.MessageCollector(this.getSessionId(), this.getInstanceId(), chatId, filter, options, events_1.ev);
+ };
+ /**
+ * [FROM DISCORDJS]
+ * Similar to createMessageCollector but in promise form.
+ * Resolves with a collection of messages that pass the specified filter.
+ * @param c The Mesasge/Chat or Chat Id to base this message colletor on
+ * @param {CollectorFilter} filter The filter function to use
+ * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector
+ * @returns {Promise>}
+ * @example
+ * ```javascript
+ * // Await !vote messages
+ * const filter = m => m.body.startsWith('!vote');
+ * // Errors: ['time'] treats ending because of the time limit as an error
+ * channel.awaitMessages(filter, { max: 4, time: 60000, errors: ['time'] })
+ * .then(collected => console.log(collected.size))
+ * .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`));
+ * ```
+ */
+ Client.prototype.awaitMessages = function (c, filter, options) {
+ var _this = this;
+ if (options === void 0) { options = {}; }
+ return new Promise(function (resolve, reject) {
+ var collector = _this.createMessageCollector(c, filter, options);
+ collector.once('end', function (collection, reason) {
+ if (options.errors && options.errors.includes(reason)) {
+ reject(collection);
+ }
+ else {
+ resolve(collection);
+ }
+ });
+ });
+ };
+ /**
+ * 处理来自Chatwoot的webhook
+ * @param webhookData The webhook payload from Chatwoot
+ */
+ Client.prototype.handleChatwootWebhook = function (webhookData) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ // 在这里实现完整的处理逻辑
+ // 例如,可以触发一个内部事件
+ logging_1.log.info("Received chatwoot webhook inside client", webhookData);
+ // @ts-ignore
+ return [4 /*yield*/, this.pup(PUPPETEER_METHODS.handleChatwootWebhook, webhookData)];
+ case 1:
+ // @ts-ignore
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ return Client;
+}());
+exports.Client = Client;
+var puppeteer_config_2 = require("../config/puppeteer.config");
+__createBinding(exports, puppeteer_config_2, "useragent");
diff --git a/src/api/Client.ts b/src/api/Client.ts
index ac309df54d..08934e4d0b 100644
--- a/src/api/Client.ts
+++ b/src/api/Client.ts
@@ -1454,6 +1454,18 @@ public async testCallback(callbackToTest: SimpleListener, testData: any) : Prom
return await pidTreeUsage([process.pid, this._page.browser().process().pid])
}
+ public async getIp(): Promise {
+ return await this._page.evaluate(async () => {
+ try {
+ const response = await fetch('https://api.ipify.org?format=json');
+ const data = await response.json();
+ return data.ip;
+ } catch (error) {
+ return error.message;
+ }
+ });
+ }
+
/**
* A list of participants in the chat who have their live location on. If the chat does not exist, or the chat does not have any contacts actively sharing their live locations, it will return false. If it's a chat with a single contact, there will be only 1 value in the array if the contact has their livelocation on.
@@ -4931,6 +4943,18 @@ public async getStatus(contactId: ContactId) : Promise<{
});
}
+ /**
+ * 处理来自Chatwoot的webhook
+ * @param webhookData The webhook payload from Chatwoot
+ */
+ public async handleChatwootWebhook(webhookData: any): Promise {
+ // 在这里实现完整的处理逻辑
+ // 例如,可以触发一个内部事件
+ log.info(`Received chatwoot webhook inside client`, webhookData);
+ // @ts-ignore
+ await this.pup(PUPPETEER_METHODS.handleChatwootWebhook, webhookData);
+ }
+
}
export { useragent } from '../config/puppeteer.config'
diff --git a/src/api/MultiSessionAPI.ts b/src/api/MultiSessionAPI.ts
new file mode 100644
index 0000000000..b7a198bd56
--- /dev/null
+++ b/src/api/MultiSessionAPI.ts
@@ -0,0 +1,728 @@
+import { Router, Request, Response } from 'express';
+import { globalSessionManager } from '../controllers/SessionManager';
+import { ConfigObject } from './model/config';
+import { log } from '../logging/logging';
+import { multiSessionChatwoot, ChatwootConfig } from '../cli/integrations/chatwoot';
+
+export interface CreateSessionRequest {
+ sessionId: string;
+ config?: Partial;
+ waitForQR?: boolean; // 是否等待QR码生成
+}
+
+export interface SessionResponse {
+ sessionId: string;
+ status: string;
+ createdAt: string;
+ lastActivity: string;
+ qrCode?: string;
+ message?: string;
+}
+
+export class MultiSessionAPI {
+ private router: Router;
+
+ constructor() {
+ this.router = Router();
+ this.setupRoutes();
+ }
+
+ private setupRoutes(): void {
+ // 创建新session
+ this.router.post('/sessions', this.createSession.bind(this));
+
+ // 获取所有sessions
+ this.router.get('/sessions', this.getAllSessions.bind(this));
+
+ // 获取特定session信息
+ this.router.get('/sessions/:sessionId', this.getSessionInfo.bind(this));
+
+ // 获取session状态
+ this.router.get('/sessions/:sessionId/status', this.getSessionStatus.bind(this));
+
+ // 获取session QR码(JSON格式)
+ this.router.get('/sessions/:sessionId/qr', this.getSessionQR.bind(this));
+
+ // 获取session QR码图片(直接返回图片)
+ this.router.get('/sessions/:sessionId/qr.png', this.getSessionQRImage.bind(this));
+
+ // 删除session
+ this.router.delete('/sessions/:sessionId', this.deleteSession.bind(this));
+
+ // 重启session(真正的重启,保持会话数据)
+ this.router.post('/sessions/:sessionId/restart', this.restartSession.bind(this));
+
+ // 发送消息
+ this.router.post('/sessions/:sessionId/send-message', this.sendMessage.bind(this));
+ this.router.post('/sessions/:id/integrations/chatwoot', this.setupChatwootIntegration.bind(this));
+ this.router.delete('/sessions/:id/integrations/chatwoot', this.removeChatwootIntegration.bind(this));
+ this.router.get('/sessions/:id/integrations/chatwoot', this.getChatwootIntegrationStatus.bind(this));
+
+ // 代理测试路由
+ this.router.post('/check-proxy', this.testProxy.bind(this));
+ this.router.post('/proxy/test', this.testHttpProxy.bind(this));
+ this.router.get('/sessions/:sessionId/ip', this.getIp.bind(this));
+ this.router.post('/sessions/:sessionId/proxy', this.updateSessionProxy.bind(this));
+
+ // Chatwoot的专用webhook - 修正路由,确保调用正确的处理程序
+ this.router.post('/sessions/:id/chatwoot/webhook', (req, res) => {
+ const { id } = req.params;
+ if (multiSessionChatwoot) {
+ // 调用正确的 webhook handler
+ return multiSessionChatwoot.createWebhookHandler(id)(req, res);
+ } else {
+ log.error('Chatwoot integration not initialized before receiving webhook.');
+ res.status(500).json({ success: false, error: 'Chatwoot integration not initialized.' });
+ }
+ });
+ }
+
+ /**
+ * 创建新session
+ */
+ private async createSession(req: Request, res: Response): Promise {
+ try {
+ const { sessionId, config = {}, waitForQR = true }: CreateSessionRequest = req.body;
+
+ if (!sessionId) {
+ res.status(400).json({
+ success: false,
+ error: 'sessionId is required'
+ });
+ return;
+ }
+
+ if (globalSessionManager.hasSession(sessionId)) {
+ res.status(409).json({
+ success: false,
+ error: `Session ${sessionId} already exists`
+ });
+ return;
+ }
+
+ log.info(`API: Creating session ${sessionId}, waitForQR: ${waitForQR}`);
+
+ const result = await globalSessionManager.createSession(sessionId, config, waitForQR);
+ // 确保将包含二维码的完整结果返回给前端
+ res.status(201).json({
+ success: result.success,
+ message: result.message,
+ qrCode: result.qrCode,
+ session: { // 同时返回会话信息以供更新
+ sessionId,
+ config,
+ status: result.qrCode ? 'qr_ready' : 'initializing',
+ createdAt: new Date(),
+ lastActivity: new Date()
+ }
+ });
+
+ } catch (error) {
+ log.error(`Create session error:`, error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 获取所有sessions
+ */
+ private getAllSessions(req: Request, res: Response): void {
+ try {
+ const sessions = globalSessionManager.getAllSessions();
+ // 直接返回真实的会话数组,不再进行任何多余的包装
+ res.status(200).json({data: sessions });
+ } catch (error) {
+ log.error('Get all sessions error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 获取特定session信息
+ */
+ private async getSessionInfo(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const session = globalSessionManager.getSession(sessionId);
+
+ if (!session) {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found`
+ });
+ return;
+ }
+
+ res.json({
+ success: true,
+ data: {
+ sessionId,
+ status: session.status,
+ createdAt: session.createdAt.toISOString(),
+ lastActivity: session.lastActivity.toISOString(),
+ config: session.config,
+ hasQRCode: !!session.qrCode,
+ qrCodeUrl: session.qrCode ? `/api/v1/sessions/${sessionId}/qr.png` : null
+ }
+ });
+
+ } catch (error) {
+ log.error('Get session info error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 获取session状态
+ */
+ private async getSessionStatus(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const session = globalSessionManager.getSession(sessionId);
+
+ if (!session) {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found`
+ });
+ return;
+ }
+
+ res.json({
+ success: true,
+ data: {
+ sessionId,
+ status: session.status,
+ lastActivity: session.lastActivity.toISOString(),
+ hasQRCode: !!session.qrCode,
+ qrCodeUrl: session.qrCode ? `/api/v1/sessions/${sessionId}/qr.png` : null
+ }
+ });
+
+ } catch (error) {
+ log.error('Get session status error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 获取session QR码(JSON格式)
+ */
+ private async getSessionQR(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const { wait = 'false', format = 'base64' } = req.query;
+
+ const session = globalSessionManager.getSession(sessionId);
+
+ if (!session) {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found`
+ });
+ return;
+ }
+
+ let qrCode = session.qrCode;
+
+ // 如果当前没有QR码但请求等待
+ if (!qrCode && wait === 'true') {
+ log.info(`API: Waiting for QR code for session ${sessionId}...`);
+ qrCode = await globalSessionManager.waitForQRCode(sessionId, 30000);
+ }
+
+ if (!qrCode) {
+ res.status(404).json({
+ success: false,
+ error: 'QR code not available',
+ status: session.status,
+ message: session.status === 'authenticated' ? 'Session is already authenticated' : 'QR code not yet generated'
+ });
+ return;
+ }
+
+ // 根据格式参数返回不同格式
+ let responseData: any = {
+ sessionId,
+ qrCode,
+ status: session.status,
+ timestamp: new Date().toISOString(),
+ format: 'base64'
+ };
+
+ if (format === 'url') {
+ responseData.qrCodeUrl = `/api/v1/sessions/${sessionId}/qr.png`;
+ responseData.format = 'url';
+ // 不包含完整的base64数据
+ delete responseData.qrCode;
+ }
+
+ res.json({
+ success: true,
+ data: responseData
+ });
+
+ } catch (error) {
+ log.error('Get session QR error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 获取session QR码图片(直接返回图片)
+ */
+ private async getSessionQRImage(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const { wait = 'false' } = req.query;
+
+ const session = globalSessionManager.getSession(sessionId);
+
+ if (!session) {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found`
+ });
+ return;
+ }
+
+ let qrCode = session.qrCode;
+
+ // 如果当前没有QR码但请求等待
+ if (!qrCode && wait === 'true') {
+ qrCode = await globalSessionManager.waitForQRCode(sessionId, 30000);
+ }
+
+ if (!qrCode) {
+ res.status(404).json({
+ success: false,
+ error: 'QR code not available',
+ status: session.status
+ });
+ return;
+ }
+
+ // 提取base64数据
+ let base64Data = qrCode;
+ if (qrCode.startsWith('data:image/png;base64,')) {
+ base64Data = qrCode.split(',')[1];
+ }
+
+ // 转换为Buffer
+ const imageBuffer = Buffer.from(base64Data, 'base64');
+
+ // 设置响应头
+ res.setHeader('Content-Type', 'image/png');
+ res.setHeader('Content-Length', imageBuffer.length);
+ res.setHeader('Cache-Control', 'no-cache');
+
+ // 返回图片
+ res.send(imageBuffer);
+
+ } catch (error) {
+ log.error('Get session QR image error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 删除session
+ */
+ private async deleteSession(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+
+ const success = await globalSessionManager.removeSession(sessionId);
+
+ if (success) {
+ res.json({
+ success: true,
+ message: `Session ${sessionId} deleted successfully`
+ });
+ } else {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found`
+ });
+ }
+
+ } catch (error) {
+ log.error('Delete session error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 重启session(真正的重启,保持会话数据)
+ */
+ private async restartSession(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const { waitForQR = true } = req.body;
+
+ if (!globalSessionManager.hasSession(sessionId)) {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found`
+ });
+ return;
+ }
+
+ log.info(`API: Restarting session ${sessionId}, waitForQR: ${waitForQR}`);
+
+ const result = await globalSessionManager.restartSession(sessionId, waitForQR);
+
+ res.json({
+ success: result.success,
+ message: result.message,
+ qrCode: result.qrCode,
+ data: {
+ sessionId,
+ restarted: true,
+ timestamp: new Date().toISOString(),
+ hasQRCode: !!result.qrCode
+ }
+ });
+
+ } catch (error) {
+ log.error(`Restart session error:`, error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 发送消息
+ */
+ private async sendMessage(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const { to, message } = req.body;
+
+ if (!to || !message) {
+ res.status(400).json({
+ success: false,
+ error: 'to and message are required'
+ });
+ return;
+ }
+
+ const client = globalSessionManager.getClient(sessionId);
+
+ if (!client) {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found or not ready`
+ });
+ return;
+ }
+
+ // 更新session活动时间
+ globalSessionManager.updateSessionActivity(sessionId);
+
+ // 发送消息
+ const result = await client.sendText(to, message);
+
+ res.json({
+ success: true,
+ data: result
+ });
+
+ } catch (error) {
+ log.error('Send message error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ private async setupChatwootIntegration(req: Request, res: Response): Promise {
+ try {
+ const { id } = req.params;
+ const { chatwootUrl, chatwootApiAccessToken, forceUpdateCwWebhook } = req.body;
+
+ if (!chatwootUrl || !chatwootApiAccessToken) {
+ res.status(400).json({ success: false, error: 'chatwootUrl and chatwootApiAccessToken are required' });
+ return;
+ }
+
+ const client = globalSessionManager.getClient(id);
+ if (!client) {
+ res.status(404).json({ success: false, error: `Session ${id} not found or not ready.` });
+ return;
+ }
+
+ const sessionConfig = globalSessionManager.getSession(id)?.config;
+
+ const chatwootConfig: ChatwootConfig = {
+ chatwootUrl,
+ chatwootApiAccessToken,
+ forceUpdateCwWebhook: !!forceUpdateCwWebhook,
+ client: client,
+ // 从session配置中获取网络参数
+ apiHost: sessionConfig.apiHost,
+ host: sessionConfig.host,
+ port: sessionConfig.port,
+ cert: sessionConfig.cert,
+ privkey: sessionConfig.privkey,
+ };
+
+ const success = await multiSessionChatwoot.initSessionChatwoot(id, chatwootConfig);
+ if (success) {
+ const webhookUrl = `${multiSessionChatwoot.config.webhookBaseUrl}/${id}/chatwoot/webhook`;
+ res.status(200).json({
+ success: true,
+ message: `Chatwoot integration enabled for session ${id}.`,
+ webhookUrl: webhookUrl
+ });
+ } else {
+ res.status(500).json({ success: false, error: 'Failed to initialize Chatwoot integration.' });
+ }
+ } catch (error) {
+ log.error('Setup Chatwoot error:', error);
+ res.status(500).json({ success: false, error: error.message });
+ }
+ }
+
+ private async removeChatwootIntegration(req: Request, res: Response): Promise {
+ try {
+ const { id } = req.params;
+ const success = await multiSessionChatwoot.removeSessionChatwoot(id);
+ if (success) {
+ res.json({ success: true, message: `Chatwoot integration removed for session ${id}.` });
+ } else {
+ res.status(404).json({ success: false, error: `Chatwoot integration not found for session ${id}.` });
+ }
+ } catch (error) {
+ log.error('Remove Chatwoot error:', error);
+ res.status(500).json({ success: false, error: error.message });
+ }
+ }
+
+ private async getChatwootIntegrationStatus(req: Request, res: Response): Promise {
+ try {
+ const { id } = req.params;
+ const status = await multiSessionChatwoot.getSessionChatwootStatus(id);
+ if (status) {
+ res.json({ success: true, data: status });
+ } else {
+ res.status(404).json({ success: false, error: `Chatwoot integration not found for session ${id}.` });
+ }
+ } catch (error) {
+ log.error('Get Chatwoot status error:', error);
+ res.status(500).json({ success: false, error: error.message });
+ }
+ }
+
+ private async testProxy(req: Request, res: Response): Promise {
+ const { server } = req.body;
+ if (!server) {
+ res.status(400).json({ success: false, error: 'Proxy server address is required.' });
+ return;
+ }
+
+ try {
+ const { SocksProxyAgent } = require('socks-proxy-agent');
+ const https = require('https');
+
+ const agent = new SocksProxyAgent(server);
+
+ const request = https.get('https://api.ipify.org?format=json', { agent, timeout: 15000 }, (response) => {
+ let data = '';
+ response.on('data', (chunk) => {
+ data += chunk;
+ });
+ response.on('end', () => {
+ try {
+ if (response.statusCode >= 200 && response.statusCode < 300) {
+ const jsonData = JSON.parse(data);
+ res.status(200).json({ success: true, ip: jsonData.ip, message: 'Proxy connection successful.' });
+ } else {
+ log.error(`Proxy test error - Status: ${response.statusCode}, Body: ${data}`);
+ res.status(500).json({ success: false, error: `Request failed with status code ${response.statusCode}` });
+ }
+ } catch (e) {
+ log.error('Proxy test error - invalid JSON:', e.message);
+ res.status(500).json({ success: false, error: 'Failed to parse response from ipify.org' });
+ }
+ });
+ });
+
+ request.on('error', (error) => {
+ log.error('Proxy test request error:', error);
+ res.status(500).json({ success: false, error: `Request error: ${error.message}`, code: error.code });
+ });
+
+ request.on('timeout', () => {
+ request.destroy();
+ log.error('Proxy test timeout');
+ res.status(500).json({ success: false, error: 'Request timed out after 15 seconds' });
+ });
+
+ } catch (error) {
+ log.error('Proxy test setup error:', error);
+ res.status(500).json({ success: false, error: `Setup error: ${error.message}` });
+ }
+ }
+
+ private async testHttpProxy(req: Request, res: Response): Promise {
+ const { host, port, username, password } = req.body;
+ if (!host || !port) {
+ res.status(400).json({ success: false, error: 'Proxy host and port are required.' });
+ return;
+ }
+
+ try {
+ const axios = require('axios');
+ const { HttpProxyAgent } = require('http-proxy-agent');
+
+ // 构造代理 URL
+ let proxyUrl;
+ if (username && password) {
+ proxyUrl = `http://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${host}:${port}`;
+ } else {
+ proxyUrl = `http://${host}:${port}`;
+ }
+
+ log.info(`Testing HTTP proxy: ${host}:${port} ${username ? 'with auth' : 'without auth'}`);
+
+ // 创建代理 agent
+ const proxyAgent = new HttpProxyAgent(proxyUrl);
+
+ const response = await axios.get('http://api.ipify.org?format=json', {
+ httpAgent: proxyAgent,
+ httpsAgent: proxyAgent,
+ timeout: 15000,
+ headers: {
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
+ }
+ });
+
+ if (response.status >= 200 && response.status < 300) {
+ log.info(`Proxy test successful, IP: ${response.data.ip}`);
+ res.status(200).json({ success: true, ip: response.data.ip, message: 'Proxy connection successful.' });
+ } else {
+ log.error(`Proxy test error - Status: ${response.status}, Body: ${JSON.stringify(response.data)}`);
+ res.status(500).json({ success: false, error: `Request failed with status code ${response.status}` });
+ }
+ } catch (error) {
+ log.error('Proxy test request error:', error.message, error.code);
+
+ let errorMessage = 'Unknown error';
+ let errorCode = error.code;
+
+ if (error.response) {
+ // Axios 响应错误
+ errorMessage = `HTTP ${error.response.status}: ${error.response.statusText}`;
+ if (error.response.data) {
+ errorMessage += ` - ${JSON.stringify(error.response.data)}`;
+ }
+ } else if (error.request) {
+ // 请求发送失败
+ if (error.code === 'ECONNREFUSED') {
+ errorMessage = '代理服务器拒绝连接,请检查代理地址和端口是否正确';
+ } else if (error.code === 'ENOTFOUND') {
+ errorMessage = '无法解析代理服务器地址,请检查主机名是否正确';
+ } else if (error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET') {
+ errorMessage = '代理连接超时,请检查网络连接或增加超时时间';
+ } else if (error.code === 'EPROTO') {
+ errorMessage = '协议错误,可能是代理认证失败或协议不匹配';
+ } else {
+ errorMessage = `网络错误: ${error.message}`;
+ }
+ } else {
+ // 其他错误
+ errorMessage = error.message;
+ }
+
+ res.status(500).json({
+ success: false,
+ error: `Request error: ${errorMessage}`,
+ code: errorCode,
+ details: {
+ proxyHost: host,
+ proxyPort: port,
+ hasAuth: !!(username && password)
+ }
+ });
+ }
+ }
+
+ private async getIp(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const client = globalSessionManager.getClient(sessionId);
+
+ if (!client) {
+ res.status(404).json({ success: false, error: `Session ${sessionId} not found or not ready.` });
+ return;
+ }
+
+ const ip = await client.getIp();
+ res.status(200).json({ success: true, ip });
+ } catch (error) {
+ log.error('Get IP error:', error);
+ res.status(500).json({ success: false, error: error.message });
+ }
+ }
+
+ private async updateSessionProxy(req: Request, res: Response): Promise {
+ try {
+ const { sessionId } = req.params;
+ const { proxy } = req.body;
+
+ const success = await globalSessionManager.updateSessionProxy(sessionId, proxy);
+
+ if (success) {
+ res.json({
+ success: true,
+ message: `Proxy for session ${sessionId} updated and session restarted.`
+ });
+ } else {
+ res.status(404).json({
+ success: false,
+ error: `Session ${sessionId} not found`
+ });
+ }
+ } catch (error) {
+ log.error('Update session proxy error:', error);
+ res.status(500).json({
+ success: false,
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * 获取路由器
+ */
+ public getRouter(): Router {
+ return this.router;
+ }
+}
+
+export const multiSessionAPI = new MultiSessionAPI();
\ No newline at end of file
diff --git a/src/api/model/aliases.js b/src/api/model/aliases.js
new file mode 100644
index 0000000000..540e35b6b3
--- /dev/null
+++ b/src/api/model/aliases.js
@@ -0,0 +1,3 @@
+"use strict";
+// declare const tag: unique symbol
+exports.__esModule = true;
diff --git a/src/api/model/button.js b/src/api/model/button.js
new file mode 100644
index 0000000000..0e345787d2
--- /dev/null
+++ b/src/api/model/button.js
@@ -0,0 +1,2 @@
+"use strict";
+exports.__esModule = true;
diff --git a/src/api/model/call.js b/src/api/model/call.js
new file mode 100644
index 0000000000..ef964560f6
--- /dev/null
+++ b/src/api/model/call.js
@@ -0,0 +1,18 @@
+"use strict";
+exports.__esModule = true;
+exports.CallState = void 0;
+var CallState;
+(function (CallState) {
+ CallState["INCOMING_RING"] = "INCOMING_RING";
+ CallState["OUTGOING_RING"] = "OUTGOING_RING";
+ CallState["OUTGOING_CALLING"] = "OUTGOING_CALLING";
+ CallState["CONNECTING"] = "CONNECTING";
+ CallState["CONNECTION_LOST"] = "CONNECTION_LOST";
+ CallState["ACTIVE"] = "ACTIVE";
+ CallState["HANDLED_REMOTELY"] = "HANDLED_REMOTELY";
+ CallState["ENDED"] = "ENDED";
+ CallState["REJECTED"] = "REJECTED";
+ CallState["REMOTE_CALL_IN_PROGRESS"] = "REMOTE_CALL_IN_PROGRESS";
+ CallState["FAILED"] = "FAILED";
+ CallState["NOT_ANSWERED"] = "NOT_ANSWERED";
+})(CallState = exports.CallState || (exports.CallState = {}));
diff --git a/src/api/model/chat.js b/src/api/model/chat.js
new file mode 100644
index 0000000000..fc27216383
--- /dev/null
+++ b/src/api/model/chat.js
@@ -0,0 +1,52 @@
+"use strict";
+exports.__esModule = true;
+exports.ChatMuteDuration = exports.ChatTypes = exports.ChatState = void 0;
+/**
+ * The ChatState represents the state you'd normally see represented under the chat name in the app.
+ */
+var ChatState;
+(function (ChatState) {
+ /**
+ * `typing...`
+ */
+ ChatState[ChatState["TYPING"] = 0] = "TYPING";
+ /**
+ * `recording audio...`
+ */
+ ChatState[ChatState["RECORDING"] = 1] = "RECORDING";
+ /**
+ * `online`
+ */
+ ChatState[ChatState["PAUSED"] = 2] = "PAUSED";
+})(ChatState = exports.ChatState || (exports.ChatState = {}));
+/**
+ * Chat types
+ * @readonly
+ * @enum {string}
+ */
+var ChatTypes;
+(function (ChatTypes) {
+ ChatTypes["SOLO"] = "solo";
+ ChatTypes["GROUP"] = "group";
+ ChatTypes["UNKNOWN"] = "unknown";
+})(ChatTypes = exports.ChatTypes || (exports.ChatTypes = {}));
+/**
+ * Valid durations for muting a chat using [[muteChat]]
+ *
+ * @readonly
+ */
+var ChatMuteDuration;
+(function (ChatMuteDuration) {
+ /**
+ * Mutes chat for 8 hours
+ */
+ ChatMuteDuration["EIGHT_HOURS"] = "EIGHT_HOURS";
+ /**
+ * Mutes chat for 1 week
+ */
+ ChatMuteDuration["ONE_WEEK"] = "ONE_WEEK";
+ /**
+ * Mutes chat forever
+ */
+ ChatMuteDuration["FOREVER"] = "FOREVER";
+})(ChatMuteDuration = exports.ChatMuteDuration || (exports.ChatMuteDuration = {}));
diff --git a/src/api/model/config.js b/src/api/model/config.js
new file mode 100644
index 0000000000..852c714544
--- /dev/null
+++ b/src/api/model/config.js
@@ -0,0 +1,111 @@
+"use strict";
+exports.__esModule = true;
+exports.LicenseType = exports.QRQuality = exports.OnError = exports.NotificationLanguage = exports.DIRECTORY_STRATEGY = exports.CLOUD_PROVIDERS = exports.QRFormat = void 0;
+/**
+ * The different types of qr code output.
+ */
+var QRFormat;
+(function (QRFormat) {
+ QRFormat["PNG"] = "png";
+ QRFormat["JPEG"] = "jpeg";
+ QRFormat["WEBM"] = "webm";
+})(QRFormat = exports.QRFormat || (exports.QRFormat = {}));
+var CLOUD_PROVIDERS;
+(function (CLOUD_PROVIDERS) {
+ CLOUD_PROVIDERS["GCP"] = "GCP";
+ CLOUD_PROVIDERS["WASABI"] = "WASABI";
+ CLOUD_PROVIDERS["AWS"] = "AWS";
+ CLOUD_PROVIDERS["CONTABO"] = "CONTABO";
+ CLOUD_PROVIDERS["DO"] = "DO";
+ CLOUD_PROVIDERS["MINIO"] = "MINIO";
+})(CLOUD_PROVIDERS = exports.CLOUD_PROVIDERS || (exports.CLOUD_PROVIDERS = {}));
+var DIRECTORY_STRATEGY;
+(function (DIRECTORY_STRATEGY) {
+ /**
+ * E.g `/2021-08-16/`
+ */
+ DIRECTORY_STRATEGY["DATE"] = "DATE";
+ /**
+ * E.g `/447123456789/`
+ */
+ DIRECTORY_STRATEGY["CHAT"] = "CHAT";
+ /**
+ * E.g `/447123456789/2021-08-16/`
+ */
+ DIRECTORY_STRATEGY["CHAT_DATE"] = "CHAT_DATE";
+ /**
+ * E.g `/2021-08-16/447123456789/`
+ */
+ DIRECTORY_STRATEGY["DATE_CHAT"] = "DATE_CHAT";
+})(DIRECTORY_STRATEGY = exports.DIRECTORY_STRATEGY || (exports.DIRECTORY_STRATEGY = {}));
+/**
+ * The available languages for the host security notification
+ */
+var NotificationLanguage;
+(function (NotificationLanguage) {
+ NotificationLanguage["PTBR"] = "pt-br";
+ NotificationLanguage["ENGB"] = "en-gb";
+ NotificationLanguage["DEDE"] = "de-de";
+ NotificationLanguage["IDID"] = "id-id";
+ NotificationLanguage["ITIT"] = "it-it";
+ NotificationLanguage["NLNL"] = "nl-nl";
+ NotificationLanguage["ES"] = "es";
+})(NotificationLanguage = exports.NotificationLanguage || (exports.NotificationLanguage = {}));
+var OnError;
+(function (OnError) {
+ /**
+ * Return it as a string
+ */
+ OnError["AS_STRING"] = "AS_STRING";
+ /**
+ * Do not log anything, just return `false`
+ */
+ OnError["RETURN_FALSE"] = "RETURN_FALSE";
+ /**
+ * throw the error
+ */
+ OnError["THROW"] = "THROW";
+ /**
+ * Log the error and return false
+ */
+ OnError["LOG_AND_FALSE"] = "LOG_AND_FALSE";
+ /**
+ * Log the error AND return the string
+ */
+ OnError["LOG_AND_STRING"] = "LOG_AND_STRING";
+ /**
+ * Return the error object
+ */
+ OnError["RETURN_ERROR"] = "RETURN_ERROR";
+ /**
+ * Do nothing.
+ */
+ OnError["NOTHING"] = "NOTHING";
+})(OnError = exports.OnError || (exports.OnError = {}));
+/**
+ * The set values of quality you can set for the quality of the qr code output. Ten being the highest quality.
+ */
+var QRQuality;
+(function (QRQuality) {
+ QRQuality[QRQuality["ONE"] = 0.1] = "ONE";
+ QRQuality[QRQuality["TWO"] = 0.2] = "TWO";
+ QRQuality[QRQuality["THREE"] = 0.3] = "THREE";
+ QRQuality[QRQuality["FOUR"] = 0.4] = "FOUR";
+ QRQuality[QRQuality["FIVE"] = 0.5] = "FIVE";
+ QRQuality[QRQuality["SIX"] = 0.6] = "SIX";
+ QRQuality[QRQuality["SEVEN"] = 0.7] = "SEVEN";
+ QRQuality[QRQuality["EIGHT"] = 0.8] = "EIGHT";
+ QRQuality[QRQuality["NINE"] = 0.9] = "NINE";
+ QRQuality[QRQuality["TEN"] = 1] = "TEN";
+})(QRQuality = exports.QRQuality || (exports.QRQuality = {}));
+var LicenseType;
+(function (LicenseType) {
+ LicenseType["CUSTOM"] = "CUSTOM";
+ LicenseType["B2B_RESTRICTED_VOLUME_LICENSE"] = "B2B_RESTRICTED_VOLUME_LICENSE";
+ LicenseType["INSIDER"] = "Insiders Program";
+ LicenseType["TEXT_STORY"] = "Text Story License Key";
+ LicenseType["IMAGE_STORY"] = "Image Story License Key";
+ LicenseType["VIDEO_STORY"] = "Video Story License Key";
+ LicenseType["PREMIUM"] = "Premium License Key";
+ LicenseType["NONE"] = "NONE";
+})(LicenseType = exports.LicenseType || (exports.LicenseType = {}));
diff --git a/src/api/model/contact.js b/src/api/model/contact.js
new file mode 100644
index 0000000000..0e345787d2
--- /dev/null
+++ b/src/api/model/contact.js
@@ -0,0 +1,2 @@
+"use strict";
+exports.__esModule = true;
diff --git a/src/api/model/errors.js b/src/api/model/errors.js
new file mode 100644
index 0000000000..906bc88732
--- /dev/null
+++ b/src/api/model/errors.js
@@ -0,0 +1,160 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+ if (ar || !(i in from)) {
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+ ar[i] = from[i];
+ }
+ }
+ return to.concat(ar || Array.prototype.slice.call(from));
+};
+exports.__esModule = true;
+exports.AddParticipantError = exports.AddParticipantErrorStatusCode = exports.CustomError = exports.ERROR_NAME = exports.SessionExpiredError = exports.PageEvaluationTimeout = void 0;
+var PageEvaluationTimeout = /** @class */ (function (_super) {
+ __extends(PageEvaluationTimeout, _super);
+ function PageEvaluationTimeout() {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ var _this = _super.apply(this, args) || this;
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(_this, PageEvaluationTimeout);
+ }
+ _this.name = 'PageEvaluationTimeout';
+ _this.message =
+ 'The method call was timed out but it may have been successfull in the WA session. It is just no longer holding up the process';
+ return _this;
+ }
+ return PageEvaluationTimeout;
+}(Error));
+exports.PageEvaluationTimeout = PageEvaluationTimeout;
+var SessionExpiredError = /** @class */ (function (_super) {
+ __extends(SessionExpiredError, _super);
+ function SessionExpiredError() {
+ var _this = _super.call(this, "This session has been deauthenticated!") || this;
+ _this.name = "SessionExpiredError"; // (2)
+ return _this;
+ }
+ return SessionExpiredError;
+}(Error));
+exports.SessionExpiredError = SessionExpiredError;
+/**
+ * Enum of error names specific to this library
+ */
+var ERROR_NAME;
+(function (ERROR_NAME) {
+ /**
+ * The sticker file exceeds the maximum 1MB limit
+ */
+ ERROR_NAME["STICKER_TOO_LARGE"] = "STICKER_TOO_LARGE";
+ /**
+ * An expected URL is missing
+ */
+ ERROR_NAME["MISSING_URL"] = "MISSING_URL";
+ /**
+ * The puppeteer page has been closed or the client has lost the connection with the page. This can happen if your computer/server has gone to sleep and waken up. Please restart your session.
+ */
+ ERROR_NAME["PAGE_CLOSED"] = "PAGE_CLOSED";
+ /**
+ * The client state is preventing the command from completing.
+ */
+ ERROR_NAME["STATE_ERROR"] = "STATE_ERROR";
+ /**
+ * The message is not a media message.
+ */
+ ERROR_NAME["NOT_MEDIA"] = "NOT_MEDIA";
+ /**
+ * Expected media is missing.
+ */
+ ERROR_NAME["MEDIA_MISSING"] = "MEDIA_MISSING";
+ /**
+ * The attempt to decrypt a sticker message has failed.
+ */
+ ERROR_NAME["STICKER_NOT_DECRYPTED"] = "STICKER_NOT_DECRYPTED";
+ /**
+ * File was not found at given path.
+ */
+ ERROR_NAME["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
+ /**
+ * The sticker metadata parameter is wrong.
+ */
+ ERROR_NAME["BAD_STICKER_METADATA"] = "BAD_STICKER_METADATA";
+ /**
+ * Unable to send text
+ */
+ ERROR_NAME["SENDTEXT_FAILURE"] = "SENDTEXT_FAILURE";
+ /**
+ * Label does not exist
+ */
+ ERROR_NAME["INVALID_LABEL"] = "INVALID_LABEL";
+})(ERROR_NAME = exports.ERROR_NAME || (exports.ERROR_NAME = {}));
+/**
+ * A simple custom error class that takes the first parameter as the name using the [[ERROR_NAME]] enum
+ */
+var CustomError = /** @class */ (function (_super) {
+ __extends(CustomError, _super);
+ function CustomError(name, message) {
+ var params = [];
+ for (var _i = 2; _i < arguments.length; _i++) {
+ params[_i - 2] = arguments[_i];
+ }
+ var _this = _super.apply(this, __spreadArray([
+ message
+ ], params, true)) || this;
+ _this.name = name;
+ _this.message = message;
+ return _this;
+ }
+ return CustomError;
+}(Error));
+exports.CustomError = CustomError;
+/**
+ * Add Participants Status Code Enum
+ */
+var AddParticipantErrorStatusCode;
+(function (AddParticipantErrorStatusCode) {
+ /**
+ * Participant could not be added to group because they are already in the group
+ */
+ AddParticipantErrorStatusCode[AddParticipantErrorStatusCode["ALREADY_IN_GROUP"] = 409] = "ALREADY_IN_GROUP";
+ /**
+ * Participant could not be added to group because their privacy settings do not allow you to add them.
+ */
+ AddParticipantErrorStatusCode[AddParticipantErrorStatusCode["PRIVACY_SETTINGS"] = 403] = "PRIVACY_SETTINGS";
+ /**
+ * Participant could not be added to group because they recently left.
+ */
+ AddParticipantErrorStatusCode[AddParticipantErrorStatusCode["RECENTLY_LEFT"] = 408] = "RECENTLY_LEFT";
+ /**
+ * Participant could not be added to group because the group is full
+ */
+ AddParticipantErrorStatusCode[AddParticipantErrorStatusCode["GROUP_FULL"] = 500] = "GROUP_FULL";
+})(AddParticipantErrorStatusCode = exports.AddParticipantErrorStatusCode || (exports.AddParticipantErrorStatusCode = {}));
+var AddParticipantError = /** @class */ (function (_super) {
+ __extends(AddParticipantError, _super);
+ function AddParticipantError(message, data) {
+ var _this = _super.call(this) || this;
+ _this.name = "ADD_PARTICIPANTS_ERROR";
+ _this.message = message;
+ _this.data = data;
+ return _this;
+ }
+ return AddParticipantError;
+}(Error));
+exports.AddParticipantError = AddParticipantError;
diff --git a/src/api/model/events.js b/src/api/model/events.js
new file mode 100644
index 0000000000..d1535fde78
--- /dev/null
+++ b/src/api/model/events.js
@@ -0,0 +1,127 @@
+"use strict";
+exports.__esModule = true;
+exports.SimpleListener = void 0;
+/**
+ * An enum of all the "simple listeners". A simple listener is a listener that just takes one parameter which is the callback function to handle the event.
+ */
+var SimpleListener;
+(function (SimpleListener) {
+ /**
+ * Represents [[onMessage]]
+ */
+ SimpleListener["Message"] = "onMessage";
+ /**
+ * Represents [[onAnyMessage]]
+ */
+ SimpleListener["AnyMessage"] = "onAnyMessage";
+ /**
+ * Represents [[onMessageDeleted]]
+ */
+ SimpleListener["MessageDeleted"] = "onMessageDeleted";
+ /**
+ * Represents [[onAck]]
+ */
+ SimpleListener["Ack"] = "onAck";
+ /**
+ * Represents [[onAddedToGroup]]
+ */
+ SimpleListener["AddedToGroup"] = "onAddedToGroup";
+ /**
+ * Represents [[onChatDeleted]]
+ */
+ SimpleListener["ChatDeleted"] = "onChatDeleted";
+ /**
+ * Represents [[onBattery]]
+ */
+ SimpleListener["Battery"] = "onBattery";
+ /**
+ * Represents [[onChatOpened]]
+ */
+ SimpleListener["ChatOpened"] = "onChatOpened";
+ /**
+ * Represents [[onIncomingCall]]
+ */
+ SimpleListener["IncomingCall"] = "onIncomingCall";
+ /**
+ * Represents [[onIncomingCall]]
+ */
+ SimpleListener["CallState"] = "onCallState";
+ /**
+ * Represents [[onGlobalParticipantsChanged]]
+ */
+ SimpleListener["GlobalParticipantsChanged"] = "onGlobalParticipantsChanged";
+ /**
+ * Represents [[onGroupApprovalRequest]]
+ */
+ SimpleListener["GroupApprovalRequest"] = "onGroupApprovalRequest";
+ /**
+ * Represents [[onChatState]]
+ */
+ SimpleListener["ChatState"] = "onChatState";
+ /**
+ * Represents [[onLogout]]
+ */
+ SimpleListener["Logout"] = "onLogout";
+ // Next two require extra params so not available to use via webhook register
+ // LiveLocation = 'onLiveLocation',
+ // ParticipantsChanged = 'onParticipantsChanged',
+ /**
+ * Represents [[onPlugged]]
+ */
+ SimpleListener["Plugged"] = "onPlugged";
+ /**
+ * Represents [[onStateChanged]]
+ */
+ SimpleListener["StateChanged"] = "onStateChanged";
+ /**
+ * Represents [[onButton]]
+ */
+ SimpleListener["Button"] = "onButton";
+ /**
+ * Represents [[onButton]]
+ */
+ SimpleListener["PollVote"] = "onPollVote";
+ /**
+ * Represents [[onBroadcast]]
+ */
+ SimpleListener["Broadcast"] = "onBroadcast";
+ /**
+ * Represents [[onLabel]]
+ */
+ SimpleListener["Label"] = "onLabel";
+ /**
+ * Requires licence
+ * Represents [[onStory]]
+ */
+ SimpleListener["Story"] = "onStory";
+ /**
+ * Requires licence
+ * Represents [[onRemovedFromGroup]]
+ */
+ SimpleListener["RemovedFromGroup"] = "onRemovedFromGroup";
+ /**
+ * Requires licence
+ * Represents [[onContactAdded]]
+ */
+ SimpleListener["ContactAdded"] = "onContactAdded";
+ /**
+ * Requires licence
+ * Represents [[onContactAdded]]
+ */
+ SimpleListener["Order"] = "onOrder";
+ /**
+ * Requires licence
+ * Represents [[onNewProduct]]
+ */
+ SimpleListener["NewProduct"] = "onNewProduct";
+ /**
+ * Requires licence
+ * Represents [[onReaction]]
+ */
+ SimpleListener["Reaction"] = "onReaction";
+ /**
+ * Requires licence
+ * Represents [[onGroupChange]]
+ */
+ SimpleListener["GroupChange"] = "onGroupChange";
+})(SimpleListener = exports.SimpleListener || (exports.SimpleListener = {}));
diff --git a/src/api/model/group-metadata.js b/src/api/model/group-metadata.js
new file mode 100644
index 0000000000..faa66d3426
--- /dev/null
+++ b/src/api/model/group-metadata.js
@@ -0,0 +1,25 @@
+"use strict";
+exports.__esModule = true;
+exports.GroupNotificationTypes = exports.groupChangeEvent = void 0;
+var groupChangeEvent;
+(function (groupChangeEvent) {
+ groupChangeEvent["remove"] = "remove";
+ groupChangeEvent["add"] = "add";
+})(groupChangeEvent = exports.groupChangeEvent || (exports.groupChangeEvent = {}));
+/**
+ * Group notification types
+ * @readonly
+ * @enum {string}
+ */
+var GroupNotificationTypes;
+(function (GroupNotificationTypes) {
+ GroupNotificationTypes["ADD"] = "add";
+ GroupNotificationTypes["INVITE"] = "invite";
+ GroupNotificationTypes["REMOVE"] = "remove";
+ GroupNotificationTypes["LEAVE"] = "leave";
+ GroupNotificationTypes["SUBJECT"] = "subject";
+ GroupNotificationTypes["DESCRIPTION"] = "description";
+ GroupNotificationTypes["PICTURE"] = "picture";
+ GroupNotificationTypes["ANNOUNCE"] = "announce";
+ GroupNotificationTypes["RESTRICT"] = "restrict";
+})(GroupNotificationTypes = exports.GroupNotificationTypes || (exports.GroupNotificationTypes = {}));
diff --git a/src/api/model/id.js b/src/api/model/id.js
new file mode 100644
index 0000000000..0e345787d2
--- /dev/null
+++ b/src/api/model/id.js
@@ -0,0 +1,2 @@
+"use strict";
+exports.__esModule = true;
diff --git a/src/api/model/index.js b/src/api/model/index.js
new file mode 100644
index 0000000000..af1f31911b
--- /dev/null
+++ b/src/api/model/index.js
@@ -0,0 +1,128 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __exportStar = (this && this.__exportStar) || function(m, exports) {
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
+};
+exports.__esModule = true;
+exports.STATE = exports.Events = exports.Status = void 0;
+__exportStar(require("./chat"), exports);
+__exportStar(require("./call"), exports);
+__exportStar(require("./contact"), exports);
+__exportStar(require("./message"), exports);
+__exportStar(require("./errors"), exports);
+__exportStar(require("./events"), exports);
+__exportStar(require("./product"), exports);
+__exportStar(require("./reactions"), exports);
+/**
+ * Client status
+ * @readonly
+ * @enum {number}
+ */
+var Status;
+(function (Status) {
+ Status[Status["INITIALIZING"] = 0] = "INITIALIZING";
+ Status[Status["AUTHENTICATING"] = 1] = "AUTHENTICATING";
+ Status[Status["READY"] = 3] = "READY";
+})(Status = exports.Status || (exports.Status = {}));
+;
+/**
+ * Events that can be emitted by the client
+ * @readonly
+ * @enum {string}
+ */
+var Events;
+(function (Events) {
+ Events["AUTHENTICATED"] = "authenticated";
+ Events["AUTHENTICATION_FAILURE"] = "auth_failure";
+ Events["READY"] = "ready";
+ Events["MESSAGE_RECEIVED"] = "message";
+ Events["MESSAGE_CREATE"] = "message_create";
+ Events["MESSAGE_REVOKED_EVERYONE"] = "message_revoke_everyone";
+ Events["MESSAGE_REVOKED_ME"] = "message_revoke_me";
+ Events["MESSAGE_ACK"] = "message_ack";
+ Events["GROUP_JOIN"] = "group_join";
+ Events["GROUP_LEAVE"] = "group_leave";
+ Events["GROUP_UPDATE"] = "group_update";
+ Events["QR_RECEIVED"] = "qr";
+ Events["DISCONNECTED"] = "disconnected";
+ Events["STATE_CHANGED"] = "change_state";
+})(Events = exports.Events || (exports.Events = {}));
+;
+/**
+ * The state of the WA Web session. You can listen to session state changes using [[onStateChanged]]. Just to be clear, some of these states aren't understood completely.
+ * @readonly
+ * @enum {string}
+ */
+var STATE;
+(function (STATE) {
+ /**
+ * Another WA web session has been opened for this account somewhere else.
+ */
+ STATE["CONFLICT"] = "CONFLICT";
+ /**
+ * The session is successfully connected and ready to send and receive messages.
+ */
+ STATE["CONNECTED"] = "CONNECTED";
+ /**
+ * WA web updates every fortnight (or so). This state would be emitted then.
+ */
+ STATE["DEPRECATED_VERSION"] = "DEPRECATED_VERSION";
+ /**
+ * This probably shows up when reloading an already authenticated session.
+ */
+ STATE["OPENING"] = "OPENING";
+ /**
+ * This probably shows up immediately after the QR code is scanned
+ */
+ STATE["PAIRING"] = "PAIRING";
+ /**
+ * This state probably represented a block on the proxy address your app is using.
+ */
+ STATE["PROXYBLOCK"] = "PROXYBLOCK";
+ /**
+ * This usually shows up when the session has been blocked by WA due to some issue with the browser/user agent. This is a different version of a Terms of Service Block from what we know. It may also show up when the host account is banned.
+ */
+ STATE["SMB_TOS_BLOCK"] = "SMB_TOS_BLOCK";
+ /**
+ * The trigger for this state is as of yet unknown
+ */
+ STATE["TIMEOUT"] = "TIMEOUT";
+ /**
+ * This usually shows up when the session has been blocked by WA due to some issue with the browser/user agent. It literally stands for Terms of Service Block. It may also show up when the host account is banned.
+ */
+ STATE["TOS_BLOCK"] = "TOS_BLOCK";
+ /**
+ * The same (probably replacement) for CONFLICT
+ */
+ STATE["UNLAUNCHED"] = "UNLAUNCHED";
+ /**
+ * When `UNPAIRED` the page is waiting for a QR Code scan. If your state becomes `UNPAIRED` then the session is most likely signed out by the host account.
+ */
+ STATE["UNPAIRED"] = "UNPAIRED";
+ /**
+ * This state is fired when the QR code has not been scanned for a long time (about 1 minute). On the page it will show "Click to reload QR code"
+ */
+ STATE["UNPAIRED_IDLE"] = "UNPAIRED_IDLE";
+ /**
+ * This is fired when the QR code is scanned
+ */
+ STATE["SYNCING"] = "SYNCING";
+ /**
+ * This is fired when the connection between web and the host account primary device is disconnected. This is fired frequently to save battery.
+ */
+ STATE["DISCONNECTED"] = "DISCONNECTED";
+})(STATE = exports.STATE || (exports.STATE = {}));
+__exportStar(require("./config"), exports);
+__exportStar(require("./media"), exports);
+__exportStar(require("./aliases"), exports);
+__exportStar(require("./label"), exports);
diff --git a/src/api/model/label.js b/src/api/model/label.js
new file mode 100644
index 0000000000..0e345787d2
--- /dev/null
+++ b/src/api/model/label.js
@@ -0,0 +1,2 @@
+"use strict";
+exports.__esModule = true;
diff --git a/src/api/model/media.js b/src/api/model/media.js
new file mode 100644
index 0000000000..da481899a8
--- /dev/null
+++ b/src/api/model/media.js
@@ -0,0 +1,12 @@
+"use strict";
+exports.__esModule = true;
+exports.defaultProcessOptions = void 0;
+exports.defaultProcessOptions = {
+ fps: 10,
+ startTime: "00:00:00.0",
+ endTime: "00:00:05.0",
+ loop: 0,
+ crop: true,
+ log: false,
+ square: 512
+};
diff --git a/src/api/model/message.js b/src/api/model/message.js
new file mode 100644
index 0000000000..ab7176846e
--- /dev/null
+++ b/src/api/model/message.js
@@ -0,0 +1,40 @@
+"use strict";
+exports.__esModule = true;
+exports.MessageAck = exports.MessageTypes = void 0;
+/**
+ * Message types
+ * @readonly
+ * @enum {string}
+ */
+var MessageTypes;
+(function (MessageTypes) {
+ MessageTypes["TEXT"] = "chat";
+ MessageTypes["AUDIO"] = "audio";
+ MessageTypes["VOICE"] = "ptt";
+ MessageTypes["IMAGE"] = "image";
+ MessageTypes["VIDEO"] = "video";
+ MessageTypes["DOCUMENT"] = "document";
+ MessageTypes["STICKER"] = "sticker";
+ MessageTypes["LOCATION"] = "location";
+ MessageTypes["CONTACT_CARD"] = "vcard";
+ MessageTypes["CONTACT_CARD_MULTI"] = "multi_vcard";
+ MessageTypes["REVOKED"] = "revoked";
+ MessageTypes["ORDER"] = "order";
+ MessageTypes["BUTTONS_RESPONSE"] = "buttons_response";
+ MessageTypes["LIST_RESPONSE"] = "list_response";
+ MessageTypes["UNKNOWN"] = "unknown";
+})(MessageTypes = exports.MessageTypes || (exports.MessageTypes = {}));
+/**
+ * Message ACK
+ * @readonly
+ * @enum {number}
+ */
+var MessageAck;
+(function (MessageAck) {
+ MessageAck[MessageAck["ACK_ERROR"] = -1] = "ACK_ERROR";
+ MessageAck[MessageAck["ACK_PENDING"] = 0] = "ACK_PENDING";
+ MessageAck[MessageAck["ACK_SERVER"] = 1] = "ACK_SERVER";
+ MessageAck[MessageAck["ACK_DEVICE"] = 2] = "ACK_DEVICE";
+ MessageAck[MessageAck["ACK_READ"] = 3] = "ACK_READ";
+ MessageAck[MessageAck["ACK_PLAYED"] = 4] = "ACK_PLAYED";
+})(MessageAck = exports.MessageAck || (exports.MessageAck = {}));
diff --git a/src/api/model/product.js b/src/api/model/product.js
new file mode 100644
index 0000000000..0e345787d2
--- /dev/null
+++ b/src/api/model/product.js
@@ -0,0 +1,2 @@
+"use strict";
+exports.__esModule = true;
diff --git a/src/api/model/reactions.js b/src/api/model/reactions.js
new file mode 100644
index 0000000000..0e345787d2
--- /dev/null
+++ b/src/api/model/reactions.js
@@ -0,0 +1,2 @@
+"use strict";
+exports.__esModule = true;
diff --git a/src/api/model/sessionInfo.js b/src/api/model/sessionInfo.js
new file mode 100644
index 0000000000..0e345787d2
--- /dev/null
+++ b/src/api/model/sessionInfo.js
@@ -0,0 +1,2 @@
+"use strict";
+exports.__esModule = true;
diff --git a/src/build/build-postman.js b/src/build/build-postman.js
new file mode 100644
index 0000000000..9edbcdcfbd
--- /dev/null
+++ b/src/build/build-postman.js
@@ -0,0 +1,306 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.generatePostmanJson = void 0;
+var fs = require("fs");
+var path = require("path");
+var parse_url_1 = require("parse-url");
+var noCase;
+var format = function (s) { return s === null || s === void 0 ? void 0 : s.replace(/[[/g,'').replace(/]]/g, '').replace(/@param/g, 'Parameter:'); };
+var ignoredMethods = [
+ 'pup',
+ 'loaded',
+ 'createMessageCollector',
+ 'awaitMessages',
+ "logger"
+];
+var aliasExamples = {
+ "ChatId": "00000000000@c.us or 00000000000-111111111@g.us",
+ "GroupChatId": "00000000000-111111111@g.us",
+ "Content": 'Hello World!',
+ "DataURL": 'data:[][;base64],',
+ "Base64": "Learn more here: https://developer.mozilla.org/en-US/docs/Glossary/Base64",
+ "MessageId": "false_447123456789@c.us_9C4D0965EA5C09D591334AB6BDB07FEB",
+ "ContactId": "00000000000@c.us"
+};
+var paramNameExamples = {
+ "ChatId": "00000000000@c.us or 00000000000-111111111@g.us"
+};
+var primatives = [
+ 'number',
+ 'string',
+ 'boolean'
+];
+function getMethodsWithDocs() {
+ var _a;
+ return __awaiter(this, void 0, void 0, function () {
+ var Project, project, res, fp, sourceFile, _i, _b, method;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0: return [4 /*yield*/, Promise.resolve().then(function () { return require("ts-morph"); })];
+ case 1:
+ Project = (_c.sent()).Project;
+ project = new Project({
+ compilerOptions: {
+ target: 99
+ }
+ });
+ res = [];
+ fp = fs.existsSync(path.resolve(__dirname, '../api/Client.d.ts')) ? '../api/Client.d.ts' : '../api/Client.ts';
+ sourceFile = project.addSourceFileAtPath(path.resolve(__dirname, fp));
+ for (_i = 0, _b = sourceFile.getClass('Client').getMethods(); _i < _b.length; _i++) {
+ method = _b[_i];
+ if (!method.hasModifier(120)) {
+ res.push({
+ name: method.getName(),
+ parameters: method.getParameters().map(function (param) {
+ var _a, _b, _c, _d;
+ return {
+ name: param.getName(),
+ type: ((_a = param.getTypeNode()) === null || _a === void 0 ? void 0 : _a.getText()) || ((_d = (((_b = param.getType()) === null || _b === void 0 ? void 0 : _b.getAliasSymbol()) || ((_c = param.getType()) === null || _c === void 0 ? void 0 : _c.getSymbol()))) === null || _d === void 0 ? void 0 : _d.getEscapedName()),
+ isOptional: param.isOptional()
+ };
+ }),
+ text: format((_a = method.getJsDocs()[0]) === null || _a === void 0 ? void 0 : _a.getInnerText())
+ });
+ }
+ }
+ return [2 /*return*/, res];
+ }
+ });
+ });
+}
+var generatePostmanJson = function (setup) {
+ if (setup === void 0) { setup = {}; }
+ return __awaiter(void 0, void 0, void 0, function () {
+ var parsed, s, x, postmanGen, pm, postmanWrap, res;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!!noCase) return [3 /*break*/, 2];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require("change-case"); })];
+ case 1:
+ noCase = (_a.sent()).noCase;
+ _a.label = 2;
+ case 2:
+ if (setup === null || setup === void 0 ? void 0 : setup.apiHost) {
+ if (setup.apiHost.includes(setup.sessionId)) {
+ parsed = (0, parse_url_1["default"])(setup.apiHost);
+ setup.host = parsed.resource;
+ setup.port = parsed.port;
+ }
+ }
+ return [4 /*yield*/, getMethodsWithDocs()];
+ case 3:
+ s = _a.sent();
+ x = s.filter(function (_a) {
+ var visibility = _a.visibility;
+ return visibility == 2 || visibility == undefined;
+ }).filter(function (_a) {
+ var name = _a.name;
+ return !name.startsWith('on');
+ }).filter(function (_a) {
+ var name = _a.name;
+ return !ignoredMethods.includes(name);
+ }).map(function (method) { return (__assign({ text: s[method.name] || '' }, method)); });
+ postmanGen = postmanRequestGeneratorGenerator(setup);
+ pm = x.map(postmanGen);
+ postmanWrap = postmanWrapGen(setup);
+ res = postmanWrap(pm);
+ if (!(setup === null || setup === void 0 ? void 0 : setup.skipSavePostmanCollection))
+ fs.writeFileSync("./open-wa-".concat(setup.sessionId, ".postman_collection.json"), JSON.stringify(res));
+ return [2 /*return*/, res];
+ }
+ });
+ });
+};
+exports.generatePostmanJson = generatePostmanJson;
+function escape(key, val) {
+ if (typeof (val) != "string")
+ return val;
+ return val
+ .replace(/["]/g, '\\"')
+ .replace(/[\\]/g, '\\\\')
+ .replace(/[/]/g, '\\/')
+ .replace(/[\b]/g, '\\b')
+ .replace(/[\f]/g, '\\f')
+ .replace(/[\n]/g, '\\n')
+ .replace(/[\r]/g, '\\r')
+ .replace(/[\t]/g, '\\t');
+}
+var postmanRequestGeneratorGenerator = function (setup) { return function (method) {
+ // if(!noCase) noCase = await import("change-case");
+ var args = {};
+ method.parameters.forEach(function (param) {
+ args[param.name] = aliasExamples[param.type] ? aliasExamples[param.type] : paramNameExamples[param.name] ? paramNameExamples[param.name] : primatives.includes(param.type) ? param.type : 'Check documentation in description';
+ });
+ var hostpath = setup.apiHost ? (0, parse_url_1["default"])(setup.apiHost).pathname.substring(1) : false;
+ var url = {
+ "raw": setup.apiHost ? "{{address}}:{{port}}".concat(hostpath ? "/".concat(hostpath) : '', "/").concat(method.name) : (setup === null || setup === void 0 ? void 0 : setup.useSessionIdInPath) ? "{{address}}:{{port}}/{{sessionId}}/" + method.name : "{{address}}:{{port}}/" + method.name,
+ "host": [
+ "{{address}}"
+ ],
+ "port": "{{port}}",
+ "path": (setup === null || setup === void 0 ? void 0 : setup.apiHost) ? [
+ (0, parse_url_1["default"])(setup.apiHost).pathname.substring(1),
+ "" + method.name
+ ].filter(function (x) { return x; }) : (setup === null || setup === void 0 ? void 0 : setup.useSessionIdInPath) ? [
+ "{{sessionId}}",
+ "" + method.name
+ ] : ["" + method.name]
+ };
+ var name = noCase(method.name).replace(/\b[a-z]|['_][a-z]|\B[A-Z]/g, function (x) { return x[0] === "'" || x[0] === "_" ? x : String.fromCharCode(x.charCodeAt(0) ^ 32); });
+ var request = {
+ "auth": {
+ "type": "apikey",
+ "apikey": [
+ {
+ "key": "value",
+ "value": setup === null || setup === void 0 ? void 0 : setup.preAuthDocs && setup.key,
+ "type": "string"
+ },
+ {
+ "key": "key",
+ "value": "key",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "name": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": JSON.stringify({ args: args }, escape, 4),
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ url: url,
+ "documentationUrl": "https://docs.openwa.dev/docs/reference/api/Client/classes/Client#".concat((method.name || "").toLowerCase()),
+ "description": "".concat(method.text, "\n[External Documentation](https://docs.openwa.dev/docs/reference/api/Client/classes/Client#").concat((method.name || "").toLowerCase(), ")")
+ };
+ if (!(setup === null || setup === void 0 ? void 0 : setup.key))
+ delete request.auth;
+ if (method.parameters.length === 0) {
+ request.body.raw = "{}";
+ // delete request.body;
+ }
+ var resp = {
+ name: name,
+ "originalRequest": request,
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": request.header,
+ "cookie": [],
+ "body": "{\n \"success\": true\n}"
+ };
+ return {
+ name: name,
+ request: request,
+ "response": [resp]
+ };
+}; };
+var postmanWrapGen = function (setup) { return function (item) { return ({
+ "info": {
+ "_postman_id": "0df31aa3-b3ce-4f20-b042-0882db0fd3a2",
+ "name": "@open-wa - ".concat(setup.sessionId),
+ "description": "Requests for use with open-wa",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+ },
+ item: item,
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "id": "d1f29ae1-7df9-46d0-8f67-4f53f562ad7e",
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "id": "370a2b6c-41d3-418f-ad9b-0404865429ce",
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ }
+ ],
+ "variable": [
+ {
+ "id": "43c133cc-7b9f-4dbe-a513-8832b664adb4",
+ "key": "address",
+ "value": (setup === null || setup === void 0 ? void 0 : setup.host) || "localhost"
+ },
+ {
+ "id": "fda078e5-712a-41bf-9da0-468fe3586d18",
+ "key": "port",
+ "value": (setup === null || setup === void 0 ? void 0 : setup.port) || "8008"
+ },
+ {
+ "id": "c1573a97-c016-4cf4-8b29-938c45146d04",
+ "key": "sessionId",
+ "value": (setup === null || setup === void 0 ? void 0 : setup.sessionId) || "session"
+ }
+ ],
+ "protocolProfileBehavior": {}
+}); }; };
diff --git a/src/cli/index.ts b/src/cli/index.ts
index 2776b8345d..ba1641386f 100644
--- a/src/cli/index.ts
+++ b/src/cli/index.ts
@@ -199,7 +199,7 @@ async function start() {
const statsLink = terminalLink('API Stats', swaggerStatsUrl);
spinner.succeed(`\n\t${statsLink}`)
}
- if(cliConfig?.chatwootUrl) await setupChatwootOutgoingMessageHandler(cliConfig, client);
+ if(cliConfig?.chatwootUrl) await setupChatwootOutgoingMessageHandler(cliConfig as any, client);
}
await ready({...createConfig, ...cliConfig, ...client.getSessionInfo(), hostAccountNumber: await client.getHostNumber()});
if (cliConfig.emitUnread) {
diff --git a/src/cli/integrations/chatwoot.ts b/src/cli/integrations/chatwoot.ts
index fca84780f2..3eb026e205 100644
--- a/src/cli/integrations/chatwoot.ts
+++ b/src/cli/integrations/chatwoot.ts
@@ -6,6 +6,7 @@ import { default as axios } from 'axios'
import { default as FormData } from 'form-data'
import mime from 'mime-types';
import { timeout } from '../../utils/tools'
+import { globalSessionManager } from '../../controllers/SessionManager';
const contactReg = {
//WID : chatwoot contact ID
@@ -126,8 +127,8 @@ export const chatwootMiddleware: (cliConfig: cliFlags, client: Client) => expres
}
}
-export const setupChatwootOutgoingMessageHandler: (cliConfig: cliFlags, client: Client) => Promise = async (cliConfig: cliFlags, client: Client) => {
- chatwootClient = new ChatwootClient(cliConfig as ChatwootConfig,client);
+export const setupChatwootOutgoingMessageHandler: (cliConfig: ChatwootConfig, client: Client) => Promise = async (cliConfig, client) => {
+ chatwootClient = new ChatwootClient(cliConfig, client);
await chatwootClient.init()
return;
}
@@ -172,7 +173,8 @@ export type ChatwootConfig = {
/**
* port
*/
- port: number
+ port: number,
+ client?: Client; // 添加可选的client属性
}
class ChatwootClient {
@@ -186,7 +188,8 @@ class ChatwootClient {
port: number;
forceUpdateCwWebhook?: boolean;
key: string;
- client: Client
+ client: Client;
+ webhookVerified = false; // 添加属性
constructor(cliConfig: ChatwootConfig, client: Client) {
const u = cliConfig.chatwootUrl as string //e.g `"localhost:3000/api/v1/accounts/3"
@@ -195,7 +198,7 @@ class ChatwootClient {
this.origin = _u.origin;
this.port = Number(_u.port || 80);
this.client = client;
- this.expectedSelfWebhookUrl = cliConfig.apiHost ? `${cliConfig.apiHost}/chatwoot ` : `${(cliConfig.host as string).includes('http') ? '' : `http${cliConfig.https || (cliConfig.cert && cliConfig.privkey) ? 's' : ''}://`}${cliConfig.host}:${cliConfig.port}/chatwoot `;
+ this.expectedSelfWebhookUrl = cliConfig.apiHost ? `${cliConfig.apiHost}` : `${(cliConfig.host as string).includes('http') ? '' : `http${cliConfig.https || (cliConfig.cert && cliConfig.privkey) ? 's' : ''}://`}${cliConfig.host}:${cliConfig.port}`;
this.expectedSelfWebhookUrl = this.expectedSelfWebhookUrl.trim()
this.key = cliConfig.key
if (cliConfig.key) this.expectedSelfWebhookUrl = `${this.expectedSelfWebhookUrl}?api_key=${cliConfig.key}`
@@ -324,9 +327,10 @@ class ChatwootClient {
const checkCodeResponse = await checkCodePromise;
if (checkCodeResponse && checkCodeResponse[0]?.checkCode == checkCode) resolve(true); else reject(`Webhook check code is incorrect. Expected ${checkCode} - incoming ${((checkCodeResponse || [])[0] || {}).checkCode}`)
})
+ this.webhookVerified = true; // 验证成功后设置为true
log.info('Chatwoot webhook verification successful')
} catch (error) {
- cancelCheckProm()
+ this.webhookVerified = false;
const e = `Unable to verify the chatwoot webhook URL on this inbox: ${error.message}`;
console.error(e)
log.error(e)
@@ -677,4 +681,129 @@ class ChatwootClient {
}
-}
\ No newline at end of file
+}
+
+// 添加多session支持的Chatwoot集成类
+export class MultiSessionChatwootIntegration {
+ public config: any; // 设为public
+ private chatwootClients: Map = new Map();
+
+ constructor(config: any) {
+ this.config = config;
+ }
+
+ /**
+ * 为指定session初始化Chatwoot集成
+ */
+ async initSessionChatwoot(sessionId: string, chatwootConfig: ChatwootConfig): Promise {
+ try {
+ const client = chatwootConfig.client || globalSessionManager.getClient(sessionId);
+ if (!client) {
+ log.error(`Cannot find client for session ${sessionId}`);
+ return false;
+ }
+
+ const sessionChatwootConfig: ChatwootConfig = {
+ ...chatwootConfig,
+ //确保使用完整的、正确的webhook URL
+ apiHost: `${this.config.webhookBaseUrl}/${sessionId}/chatwoot/webhook`,
+ };
+
+ const chatwootClient = new ChatwootClient(sessionChatwootConfig, client);
+ await chatwootClient.init();
+ this.chatwootClients.set(sessionId, chatwootClient);
+ log.info(`Chatwoot integration initialized for session: ${sessionId}`);
+ return true;
+ } catch (error) {
+ log.error(`Failed to initialize Chatwoot for session ${sessionId}:`, error);
+ return false;
+ }
+ }
+
+ /**
+ * 为session移除Chatwoot集成
+ */
+ async removeSessionChatwoot(sessionId: string): Promise {
+ try {
+ if (this.chatwootClients.has(sessionId)) {
+ this.chatwootClients.delete(sessionId);
+ log.info(`Chatwoot integration removed for session: ${sessionId}`);
+ return true;
+ }
+ return false;
+ } catch (error) {
+ log.error(`Failed to remove Chatwoot for session ${sessionId}:`, error);
+ return false;
+ }
+ }
+
+ /**
+ * 获取session的Chatwoot客户端
+ */
+ getChatwootClient(sessionId: string): ChatwootClient | undefined {
+ return this.chatwootClients.get(sessionId);
+ }
+
+ /**
+ * 处理来自Chatwoot的webhook消息
+ */
+ createWebhookHandler(sessionId: string) {
+ return async (req: Request, res: Response): Promise>> => {
+ // 在此处添加详细的日志记录
+ log.info(`[Chatwoot Webhook] Received request for session: ${sessionId}`);
+ log.info(`[Chatwoot Webhook] Headers: ${JSON.stringify(req.headers, null, 2)}`);
+ log.info(`[Chatwoot Webhook] Body: ${JSON.stringify(req.body, null, 2)}`);
+
+ const chatwootClient = this.chatwootClients.get(sessionId);
+ if (!chatwootClient) {
+ log.error(`[Chatwoot Webhook] No Chatwoot integration found for session: ${sessionId}`);
+ return res.status(404).json({
+ error: `No Chatwoot integration found for session: ${sessionId}`
+ });
+ }
+
+ const client = globalSessionManager.getClient(sessionId);
+ if (!client) {
+ log.error(`[Chatwoot Webhook] Session ${sessionId} not found or not ready`);
+ return res.status(404).json({
+ error: `Session ${sessionId} not found or not ready`
+ });
+ }
+
+ // 使用原有的chatwootMiddleware逻辑,但针对特定session
+ log.info(`[Chatwoot Webhook] Forwarding to chatwootMiddleware for session: ${sessionId}`);
+ return chatwootMiddleware({ key: this.config.key }, client)(req, res);
+ };
+ }
+
+ /**
+ * 获取所有活跃的Chatwoot集成
+ */
+ getActiveChatwootSessions(): string[] {
+ return Array.from(this.chatwootClients.keys());
+ }
+
+ public async getSessionChatwootStatus(sessionId: string): Promise {
+ const client = this.chatwootClients.get(sessionId);
+ if(!client) return null;
+ return {
+ inboxId: client.inboxId,
+ accountId: client.accountId,
+ webhookUrl: client.expectedSelfWebhookUrl,
+ isWebhookVerified: client.webhookVerified,
+ }
+ }
+}
+
+// 全局多session Chatwoot集成实例
+export let multiSessionChatwoot: MultiSessionChatwootIntegration | null = null;
+
+/**
+ * 初始化多session Chatwoot集成
+ */
+export const initMultiSessionChatwoot = (config: any): MultiSessionChatwootIntegration => {
+ if (!multiSessionChatwoot) {
+ multiSessionChatwoot = new MultiSessionChatwootIntegration(config);
+ }
+ return multiSessionChatwoot;
+};
\ No newline at end of file
diff --git a/src/cli/multiSessionRoutes.ts b/src/cli/multiSessionRoutes.ts
new file mode 100644
index 0000000000..a39e1102bd
--- /dev/null
+++ b/src/cli/multiSessionRoutes.ts
@@ -0,0 +1,10 @@
+import { Express } from 'express';
+import { multiSessionAPI } from '../api/MultiSessionAPI';
+
+/**
+ * 设置多Session路由
+ */
+export const setupMultiSessionRoutes = (app: Express): void => {
+ // 添加API版本前缀
+ app.use('/api/v1', multiSessionAPI.getRouter());
+};
\ No newline at end of file
diff --git a/src/cli/multiSessionServer.ts b/src/cli/multiSessionServer.ts
new file mode 100644
index 0000000000..a32e5b2bf8
--- /dev/null
+++ b/src/cli/multiSessionServer.ts
@@ -0,0 +1,178 @@
+/**
+ * 多Session模式服务器
+ * 在现有单session服务器基础上,添加多session支持
+ */
+
+import { globalSessionManager, SessionManager } from '../controllers/SessionManager';
+import { setupMultiSessionRoutes } from './multiSessionRoutes';
+import { log } from '../logging/logging';
+import { ConfigObject } from '../api/model/config';
+
+// 导入现有的服务器设置函数
+import {
+ app,
+ server,
+ setUpExpressApp,
+ setupHttpServer,
+ setupMediaMiddleware,
+ enableCORSRequests
+} from './server';
+import * as express from 'express';
+
+/**
+ * 多Session服务器配置
+ */
+export interface MultiSessionConfig {
+ port?: number;
+ host?: string;
+ enableCors?: boolean;
+ defaultSessionConfig?: Partial;
+ maxSessions?: number;
+}
+
+/**
+ * 启动多Session服务器
+ */
+export const startMultiSessionServer = async (config: MultiSessionConfig = {}) => {
+ const {
+ port = 8080,
+ host = '0.0.0.0',
+ enableCors = true,
+ defaultSessionConfig = {},
+ maxSessions = 10
+ } = config;
+
+ try {
+ // 设置Express应用
+ setUpExpressApp();
+ log.info('Express app setup complete');
+
+ // 启用CORS(如果需要)
+ if (enableCors) {
+ await enableCORSRequests({ cors: true });
+ log.info('CORS enabled');
+ }
+
+ // 设置HTTP服务器
+ await setupHttpServer({ port });
+ log.info('HTTP server setup complete');
+
+ // 设置媒体中间件
+ setupMediaMiddleware();
+ log.info('Media middleware setup complete');
+
+ // 设置多session路由
+ setupMultiSessionRoutes(app);
+ log.info('Multi-session routes setup complete');
+
+ // 初始化全局session管理器
+ const newManager = new SessionManager(defaultSessionConfig);
+ Object.assign(globalSessionManager, newManager);
+ log.info('Session manager initialized');
+
+ // 添加健康检查端点
+ app.get('/health', (req, res) => {
+ res.json({
+ status: 'ok',
+ timestamp: new Date().toISOString(),
+ sessions: {
+ count: globalSessionManager.getSessionCount(),
+ limit: maxSessions
+ }
+ });
+ });
+
+ // 添加多session信息端点
+ app.get('/info', (req, res) => {
+ res.json({
+ name: 'Open-WA Multi-Session Server',
+ version: require('../../package.json').version,
+ multiSession: true,
+ maxSessions,
+ currentSessions: globalSessionManager.getSessionCount(),
+ endpoints: {
+ sessions: '/api/v1/sessions',
+ health: '/health',
+ info: '/info'
+ }
+ });
+ });
+
+ // 启动服务器
+ return new Promise((resolve, reject) => {
+ try {
+ server.listen(port, host, () => {
+ log.info(`🚀 Multi-Session Server started successfully!`);
+ log.info(`📡 Server running at http://${host}:${port}`);
+ log.info(`📊 Health check: http://${host}:${port}/health`);
+ log.info(`📋 Session API: http://${host}:${port}/api/v1/sessions`);
+ log.info(`📖 Server info: http://${host}:${port}/info`);
+ log.info(`🔧 Max sessions: ${maxSessions}`);
+ resolve(server);
+ });
+
+ server.on('error', (error) => {
+ log.error('Server error:', error);
+ reject(error);
+ });
+
+ // 优雅关闭处理
+ const gracefulShutdown = async (signal: string) => {
+ log.info(`📥 Received ${signal}. Starting graceful shutdown...`);
+
+ // 清理所有session
+ await globalSessionManager.cleanup();
+
+ // 关闭服务器
+ server.close(() => {
+ log.info('🛑 Multi-Session Server shutdown complete');
+ process.exit(0);
+ });
+
+ // 强制退出保护
+ setTimeout(() => {
+ log.error('💥 Forced shutdown after timeout');
+ process.exit(1);
+ }, 30000);
+ };
+
+ // 注册信号处理
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
+
+ } catch (error) {
+ log.error('Failed to start server:', error);
+ reject(error);
+ }
+ });
+
+ } catch (error) {
+ log.error('❌ Failed to start Multi-Session Server:', error);
+ throw error;
+ }
+};
+
+/**
+ * CLI启动函数
+ */
+export const startFromCLI = async () => {
+ try {
+ const config: MultiSessionConfig = {
+ port: parseInt(process.env.PORT || '8080'),
+ host: process.env.HOST || '0.0.0.0',
+ enableCors: process.env.ENABLE_CORS !== 'false',
+ maxSessions: parseInt(process.env.MAX_SESSIONS || '10'),
+ defaultSessionConfig: {
+ multiDevice: true,
+ headless: process.env.HEADLESS !== 'false',
+ useChrome: process.env.USE_CHROME === 'true',
+ }
+ };
+
+ log.info('Starting server with config:', config);
+ await startMultiSessionServer(config);
+ } catch (error) {
+ log.error('Failed to start from CLI:', error);
+ process.exit(1);
+ }
+};
\ No newline at end of file
diff --git a/src/cli/server.ts b/src/cli/server.ts
index 4facbfd662..124e118909 100644
--- a/src/cli/server.ts
+++ b/src/cli/server.ts
@@ -96,10 +96,10 @@ export const setupHttpServer = (cliConfig: cliFlags) => {
server = http.createServer(app);
}
-export const setUpExpressApp : () => void = () => {
- app.use(robots({ UserAgent: '*', Disallow: '/' }))
- //@ts-ignore
- app.use(express.json({ limit: '99mb' })) //add the limit option so we can send base64 data through the api
+export const setUpExpressApp = () => {
+ app.use(robots({ UserAgent: '*', Disallow: '/' }));
+ // @ts-ignore
+ app.use(express.json({ limit: '200mb' })); //add the limit option so we can send base64 data through the api
setupMetaMiddleware();
}
diff --git a/src/config/multiSessionConfig.ts b/src/config/multiSessionConfig.ts
new file mode 100644
index 0000000000..5276fb377a
--- /dev/null
+++ b/src/config/multiSessionConfig.ts
@@ -0,0 +1,133 @@
+import { ConfigObject } from '../api/model/config';
+
+/**
+ * 多Session模式的默认配置
+ */
+export const DEFAULT_MULTI_SESSION_CONFIG: Partial = {
+ // 强制启用多设备模式
+ multiDevice: true,
+
+ // 推荐的浏览器设置
+ headless: true,
+ useChrome: false,
+
+ // 稳定性设置
+ killProcessOnBrowserClose: false,
+ restartOnCrash: false,
+
+ // 性能优化
+ disableSpins: true,
+ blockCrashLogs: true,
+
+ // 会话管理
+ sessionDataPath: './sessions',
+
+ // 默认关闭一些可能冲突的功能
+ popup: false,
+ qrTimeout: 120,
+
+ // 日志设置
+ logging: [],
+
+ // 网络设置
+ proxyServerCredentials: undefined,
+};
+
+/**
+ * 为特定session生成配置
+ */
+export const generateSessionConfig = (
+ sessionId: string,
+ customConfig: Partial = {}
+): ConfigObject => {
+ return {
+ ...DEFAULT_MULTI_SESSION_CONFIG,
+ ...customConfig,
+ sessionId,
+ // 确保session有独立的数据目录
+ sessionDataPath: customConfig.sessionDataPath || `./sessions/${sessionId}`,
+ } as ConfigObject;
+};
+
+/**
+ * 验证多session配置
+ */
+export const validateMultiSessionConfig = (config: Partial): {
+ valid: boolean;
+ errors: string[];
+ warnings: string[];
+} => {
+ const errors: string[] = [];
+ const warnings: string[] = [];
+
+ // 必须启用multiDevice
+ if (config.multiDevice !== true) {
+ errors.push('multiDevice must be true for multi-session mode');
+ }
+
+ // 检查端口冲突
+ if (config.port && (config.port < 1024 || config.port > 65535)) {
+ errors.push('port must be between 1024 and 65535');
+ }
+
+ // 检查sessionId
+ if (!config.sessionId) {
+ errors.push('sessionId is required');
+ } else if (!/^[a-zA-Z0-9_-]+$/.test(config.sessionId)) {
+ errors.push('sessionId must contain only alphanumeric characters, hyphens, and underscores');
+ }
+
+ // 性能相关警告
+ if (config.headless === false) {
+ warnings.push('Running in non-headless mode may impact performance with multiple sessions');
+ }
+
+ if (config.useChrome === true && config.chromiumArgs) {
+ warnings.push('Custom chromium args with useChrome may cause issues');
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ warnings
+ };
+};
+
+/**
+ * 多Session环境变量配置
+ */
+export const MULTI_SESSION_ENV_DEFAULTS = {
+ // 服务器设置
+ PORT: '8081',
+ HOST: '0.0.0.0',
+ MAX_SESSIONS: '10',
+
+ // WhatsApp设置
+ MULTI_DEVICE: 'true',
+ HEADLESS: 'true',
+ USE_CHROME: 'false',
+
+ // 功能设置
+ ENABLE_CORS: 'true',
+ DISABLE_SPINS: 'true',
+ BLOCK_CRASH_LOGS: 'true',
+
+ // 会话设置
+ SESSION_DATA_PATH: './sessions',
+ QR_TIMEOUT: '120',
+} as const;
+
+/**
+ * 从环境变量获取多session配置
+ */
+export const getMultiSessionConfigFromEnv = (): Partial => {
+ return {
+ multiDevice: process.env.MULTI_DEVICE === 'true',
+ headless: process.env.HEADLESS !== 'false',
+ useChrome: process.env.USE_CHROME === 'true',
+ disableSpins: process.env.DISABLE_SPINS === 'true',
+ blockCrashLogs: process.env.BLOCK_CRASH_LOGS === 'true',
+ sessionDataPath: process.env.SESSION_DATA_PATH || MULTI_SESSION_ENV_DEFAULTS.SESSION_DATA_PATH,
+ qrTimeout: parseInt(process.env.QR_TIMEOUT || MULTI_SESSION_ENV_DEFAULTS.QR_TIMEOUT),
+ };
+};
\ No newline at end of file
diff --git a/src/config/puppeteer.config.js b/src/config/puppeteer.config.js
new file mode 100644
index 0000000000..5e7d4a20ac
--- /dev/null
+++ b/src/config/puppeteer.config.js
@@ -0,0 +1,65 @@
+"use strict";
+exports.__esModule = true;
+exports.height = exports.width = exports.puppeteerConfig = exports.useragent = exports.createUserAgent = void 0;
+var puppeteerConfig = {
+ WAUrl: 'https://web.whatsapp.com',
+ width: 1440,
+ height: 900,
+ chromiumArgs: [
+ // `--app=${WAUrl}`,
+ '--log-level=3',
+ //'--start-maximized',
+ '--no-default-browser-check',
+ '--disable-site-isolation-trials',
+ '--no-experiments',
+ '--ignore-gpu-blacklist',
+ '--ignore-certificate-errors',
+ '--ignore-certificate-errors-spki-list',
+ '--disable-gpu',
+ '--disable-extensions',
+ '--disable-default-apps',
+ '--enable-features=NetworkService',
+ '--disable-setuid-sandbox',
+ '--no-sandbox',
+ // Extras
+ '--disable-webgl',
+ '--disable-infobars',
+ '--window-position=0,0',
+ '--ignore-certifcate-errors',
+ '--ignore-certifcate-errors-spki-list',
+ '--disable-threaded-animation',
+ '--disable-threaded-scrolling',
+ '--disable-in-process-stack-traces',
+ '--disable-histogram-customizer',
+ '--disable-gl-extensions',
+ '--disable-composited-antialiasing',
+ '--disable-session-crashed-bubble',
+ '--disable-canvas-aa',
+ '--disable-3d-apis',
+ '--disable-accelerated-2d-canvas',
+ '--disable-accelerated-jpeg-decoding',
+ '--disable-accelerated-mjpeg-decode',
+ '--disable-app-list-dismiss-on-blur',
+ '--disable-accelerated-video-decode',
+ '--disable-dev-shm-usage',
+ '--js-flags=--expose-gc',
+ // '--incognito',
+ //suggested in #563
+ // '--single-process',
+ // '--no-zygote',
+ // '--renderer-process-limit=1',
+ // '--no-first-run'
+ '--disable-features=site-per-process',
+ '--disable-gl-drawing-for-tests',
+ //keep awake in all situations
+ '--disable-background-timer-throttling',
+ '--disable-backgrounding-occluded-windows',
+ '--disable-renderer-backgrounding'
+ ]
+};
+exports.puppeteerConfig = puppeteerConfig;
+var createUserAgent = function (waVersion) { return "WhatsApp/".concat(waVersion, " Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"); };
+exports.createUserAgent = createUserAgent;
+exports.useragent = (0, exports.createUserAgent)('2.2147.16');
+exports.width = puppeteerConfig.width;
+exports.height = puppeteerConfig.height;
diff --git a/src/controllers/SessionManager.ts b/src/controllers/SessionManager.ts
new file mode 100644
index 0000000000..8756f42e2c
--- /dev/null
+++ b/src/controllers/SessionManager.ts
@@ -0,0 +1,788 @@
+import { Client } from '../api/Client';
+import { create } from './initializer';
+import { ev } from './events';
+import { ConfigObject } from '../api/model/config';
+import { log } from '../logging/logging';
+import * as fs from 'fs-extra'; // Use fs-extra for robust file operations
+import * as path from 'path';
+
+export interface SessionInstance {
+ client: Client | null;
+ config: ConfigObject;
+ status: 'initializing' | 'qr_ready' | 'authenticated' | 'ready' | 'failed' | 'terminated';
+ createdAt: Date;
+ lastActivity: Date;
+ qrCode?: string; // 存储QR码
+ qrCodeUrl?: string; // 存储QR码URL
+}
+
+export class SessionManager {
+ private sessions: Map = new Map();
+ private defaultConfig: Partial;
+ private qrPromises: Map void, reject: (error: any) => void }> = new Map();
+ private isQRListenerSetup: boolean = false;
+
+ constructor(defaultConfig: Partial = {}) {
+ this.defaultConfig = defaultConfig;
+ this.setupGlobalQRCodeListener();
+ this.loadAndRestoreSessions(); // 在构造函数中调用,以恢复现有会话
+ }
+
+ /**
+ * 在服务启动时,扫描sessions目录,并恢复所有已存在的会话
+ */
+ public loadAndRestoreSessions(): void {
+ const sessionsDir = path.resolve('./sessions');
+ if (!fs.existsSync(sessionsDir)) {
+ fs.mkdirSync(sessionsDir, { recursive: true });
+ return;
+ }
+
+ const sessionFolders = fs.readdirSync(sessionsDir).filter(file =>
+ fs.statSync(path.join(sessionsDir, file)).isDirectory()
+ );
+
+ log.info(`Found ${sessionFolders.length} session folder(s): ${sessionFolders.join(', ')}`);
+
+ // 验证并恢复有效的会话
+ const validSessions: string[] = [];
+ const invalidSessions: string[] = [];
+
+ sessionFolders.forEach(sessionId => {
+ if (!this.sessions.has(sessionId)) {
+ const configPath = path.join(sessionsDir, sessionId, 'config.json');
+
+ if (!fs.existsSync(configPath)) {
+ log.warn(`Session ${sessionId} has no config.json, marking as invalid`);
+ invalidSessions.push(sessionId);
+ return;
+ }
+
+ try {
+ const configContent = fs.readFileSync(configPath, 'utf-8');
+ const config = JSON.parse(configContent);
+
+ // 检查配置文件是否有效
+ if (!config || typeof config !== 'object') {
+ throw new Error('Invalid config format');
+ }
+
+ // 检查是否有必要的字段
+ if (!config.sessionId && !sessionId) {
+ throw new Error('Missing sessionId');
+ }
+
+ // 确保代理配置被正确恢复
+ if (config.proxyServerCredentials) {
+ log.info(`Session ${sessionId} has proxy config: ${config.proxyServerCredentials.address}`);
+ // 确保启用原生代理模式
+ config.useNativeProxy = true;
+ }
+
+ log.info(`Restoring valid session: ${sessionId} with config:`, {
+ sessionId,
+ hasProxy: !!config.proxyServerCredentials,
+ proxyAddress: config.proxyServerCredentials?.address,
+ useNativeProxy: config.useNativeProxy
+ });
+ validSessions.push(sessionId);
+
+ this.createSession(sessionId, config, false).catch(error => {
+ log.error(`Failed to restore session ${sessionId}:`, error);
+ // 如果恢复失败,也将其标记为无效
+ invalidSessions.push(sessionId);
+ });
+
+ } catch (error) {
+ log.error(`Failed to parse config for session ${sessionId}:`, error);
+ invalidSessions.push(sessionId);
+ }
+ }
+ });
+
+ log.info(`Valid sessions to restore: ${validSessions.length} (${validSessions.join(', ')})`);
+
+ if (invalidSessions.length > 0) {
+ log.warn(`Invalid sessions found: ${invalidSessions.length} (${invalidSessions.join(', ')})`);
+ log.info('Cleaning up invalid session directories...');
+
+ // 异步清理无效的会话目录
+ this.cleanupInvalidSessions(invalidSessions).catch(error => {
+ log.error('Error during invalid session cleanup:', error);
+ });
+ }
+ }
+
+ /**
+ * 清理无效的会话目录
+ */
+ private async cleanupInvalidSessions(invalidSessionIds: string[]): Promise {
+ const cleanupPromises = invalidSessionIds.map(async sessionId => {
+ try {
+ log.info(`Cleaning up invalid session: ${sessionId}`);
+ const success = await this.forceCleanupSessionFiles(sessionId);
+ if (success) {
+ log.info(`Successfully cleaned up invalid session: ${sessionId}`);
+ } else {
+ log.warn(`Could not fully clean up invalid session: ${sessionId}`);
+ }
+ } catch (error) {
+ log.error(`Error cleaning up invalid session ${sessionId}:`, error);
+ }
+ });
+
+ await Promise.all(cleanupPromises);
+ log.info('Invalid session cleanup completed');
+ }
+
+ /**
+ * 设置全局QR码监听器(只设置一次)
+ */
+ private setupGlobalQRCodeListener(): void {
+ if (this.isQRListenerSetup) return;
+
+ log.info('Setting up global QR code listener');
+
+ // 监听所有QR码事件 - 按照demo中的确切格式
+ ev.on('qr.**', async (qrcode: string, sessionId: string) => {
+ log.info(`=== QR CODE EVENT RECEIVED ===`);
+ log.info(`Session ID: ${sessionId}`);
+ log.info(`QR code length: ${qrcode ? qrcode.length : 'null'}`);
+ log.info(`QR code preview: ${qrcode ? qrcode.substring(0, 80) : 'null'}...`);
+
+ // 获取session
+ const session = this.sessions.get(sessionId);
+ if (session) {
+ // 存储QR码
+ session.qrCode = qrcode;
+ session.status = 'qr_ready';
+ session.lastActivity = new Date();
+
+ log.info(`QR code stored for session: ${sessionId}`);
+
+ // 解决等待的Promise
+ const promiseHandlers = this.qrPromises.get(sessionId);
+ if (promiseHandlers) {
+ log.info(`Resolving QR promise for session: ${sessionId}`);
+ promiseHandlers.resolve(qrcode);
+ this.qrPromises.delete(sessionId);
+ log.info(`QR promise resolved for session: ${sessionId}`);
+ }
+ } else {
+ log.warn(`Unknown session: ${sessionId}`);
+ }
+ });
+
+ // 监听session data事件 - 按照官方文档
+ ev.on('sessionData.**', (sessionData: any, sessionId: string) => {
+ log.info(`Session data received for: ${sessionId}`);
+ const session = this.sessions.get(sessionId);
+ if (session) {
+ session.lastActivity = new Date();
+ log.info(`Session ${sessionId} data updated`);
+ }
+ });
+
+ // 监听认证成功事件
+ ev.on('authenticated.**', (data: any, sessionId: string) => {
+ log.info(`Session authenticated: ${sessionId}`);
+ const session = this.sessions.get(sessionId);
+ if (session) {
+ session.status = 'authenticated';
+ session.lastActivity = new Date();
+ // 清除QR码
+ delete session.qrCode;
+ delete session.qrCodeUrl;
+ log.info(`Session ${sessionId} authenticated, QR code cleared`);
+ }
+ });
+
+ // 监听连接状态变化
+ ev.on('**.**', (data: any, sessionId: string, namespace: string) => {
+ if (sessionId && this.sessions.has(sessionId)) {
+ log.info(`Event ${namespace} for session ${sessionId}:`, typeof data === 'string' ? data.substring(0, 100) : data);
+ }
+ });
+
+ this.isQRListenerSetup = true;
+ log.info('Global QR code listener setup complete');
+ }
+
+ /**
+ * 创建新的session并可选择等待QR码
+ */
+ public async createSession(sessionId: string, config: Partial = {}, waitForQR: boolean = false): Promise<{
+ success: boolean;
+ message: string;
+ qrCode?: string;
+ }> {
+ if (this.sessions.has(sessionId)) {
+ throw new Error(`Session ${sessionId} already exists`);
+ }
+
+ const sessionConfig: ConfigObject = {
+ ...this.defaultConfig,
+ sessionId,
+ // 强制qrLogSkip为true,以防止在控制台打印QR码
+ qrLogSkip: true,
+ qrTimeout: config.qrTimeout || 120,
+ sessionDataPath: config.sessionDataPath || `./sessions/${sessionId}`,
+ port: config.port || this.generateUniquePort(),
+ // 如果提供了代理,则强制启用原生代理模式
+ useNativeProxy: config.useNativeProxy === true, // Only enable if explicitly set to true
+ ...config, // 将文件配置放在最后,确保其优先级最高
+ };
+
+ console.log('sessionConfig', sessionConfig);
+
+ log.info(`Creating session: ${sessionId} with config:`, {
+ sessionId,
+ qrLogSkip: sessionConfig.qrLogSkip,
+ qrTimeout: sessionConfig.qrTimeout,
+ multiDevice: sessionConfig.multiDevice,
+ waitForQR
+ });
+
+ const sessionInstance: SessionInstance = {
+ client: null,
+ config: sessionConfig,
+ status: 'initializing',
+ createdAt: new Date(),
+ lastActivity: new Date(),
+ };
+ this.sessions.set(sessionId, sessionInstance);
+
+ // 保存初始配置
+ const sessionDir = path.join('./sessions', sessionId);
+ if (!fs.existsSync(sessionDir)) {
+ fs.mkdirSync(sessionDir, { recursive: true });
+ }
+ const configPath = path.join(sessionDir, 'config.json');
+ fs.writeFileSync(configPath, JSON.stringify(sessionConfig, null, 4));
+ log.info(`Initial config for ${sessionId} saved to ${configPath}`);
+
+ // 只有在需要时才等待QR码
+ let qrCodePromise: Promise | null = null;
+ if (waitForQR) {
+ qrCodePromise = this.waitForQRCode(sessionId, 35000); // 35秒超时
+ }
+
+ // 在后台启动客户端创建
+ create(sessionConfig)
+ .then(client => {
+ log.info(`Client object created for session: ${sessionId}. Session is ready.`);
+ sessionInstance.client = client;
+ sessionInstance.status = 'ready';
+ sessionInstance.lastActivity = new Date();
+ this.setupSessionEvents(sessionId, client);
+ })
+ .catch(error => {
+ log.error(`Error during client creation for session ${sessionId}:`, error);
+ sessionInstance.status = 'failed';
+
+ // 如果有等待的Promise,则拒绝它
+ const promiseHandlers = this.qrPromises.get(sessionId);
+ if (promiseHandlers) {
+ promiseHandlers.reject(error);
+ this.qrPromises.delete(sessionId);
+ }
+ });
+
+ log.info(`Client creation for session ${sessionId} has been initiated.`);
+
+ // 如果需要,等待QR码Promise
+ if (waitForQR) {
+ log.info(`Now waiting for QR code for session ${sessionId}...`);
+ try {
+ const qrCode = await qrCodePromise;
+ if (qrCode) {
+ log.info(`QR code successfully retrieved for session ${sessionId}.`);
+ return {
+ success: true,
+ message: "QR code received.",
+ qrCode,
+ };
+ } else {
+ log.warn(`waitForQRCode returned null (timeout) for session ${sessionId}.`);
+ await this.removeSession(sessionId); // 清理失败的会话
+ return {
+ success: false,
+ message: 'Timed out waiting for QR code.',
+ };
+ }
+ } catch(error) {
+ log.error(`Error while waiting for QR code promise for session ${sessionId}:`, error);
+ await this.removeSession(sessionId); // 清理失败的会话
+ return {
+ success: false,
+ message: `Failed to create session: ${error.message}`,
+ };
+ }
+ } else {
+ return {
+ success: true,
+ message: 'Session creation initiated. Get QR code from the relevant endpoint.',
+ };
+ }
+ }
+
+ /**
+ * 等待QR码生成
+ */
+ async waitForQRCode(sessionId: string, timeout: number = 30000): Promise {
+ log.info(`=== WAIT FOR QR CODE START ===`);
+ log.info(`Session: ${sessionId}, Timeout: ${timeout}ms`);
+
+ const session = this.sessions.get(sessionId);
+ if (!session) {
+ log.error(`Session ${sessionId} not found`);
+ throw new Error(`Session ${sessionId} not found`);
+ }
+
+ // 如果已经有QR码,直接返回
+ if (session.qrCode) {
+ log.info(`QR code already available for session: ${sessionId}, returning immediately`);
+ return session.qrCode;
+ }
+
+ log.info(`Setting up QR code wait for session: ${sessionId}`);
+
+ // 等待QR码生成
+ return new Promise((resolve, reject) => {
+ log.info(`Setting promise for session: ${sessionId}`);
+ this.qrPromises.set(sessionId, { resolve, reject });
+ log.info(`Promise set. Current promises: [${Array.from(this.qrPromises.keys()).join(', ')}]`);
+
+ // 设置超时
+ setTimeout(() => {
+ if (this.qrPromises.has(sessionId)) {
+ log.warn(`=== QR CODE TIMEOUT ===`);
+ log.warn(`Session: ${sessionId} timed out after ${timeout}ms`);
+ this.qrPromises.delete(sessionId);
+ resolve(null);
+ } else {
+ log.info(`Timeout check: promise for ${sessionId} already removed (success)`);
+ }
+ }, timeout);
+
+ log.info(`=== WAIT FOR QR CODE SETUP COMPLETE ===`);
+ });
+ }
+
+ /**
+ * 获取session的QR码
+ */
+ getSessionQRCode(sessionId: string): string | null {
+ const session = this.sessions.get(sessionId);
+ return session?.qrCode || null;
+ }
+
+ /**
+ * 获取session
+ */
+ getSession(sessionId: string): SessionInstance | undefined {
+ return this.sessions.get(sessionId);
+ }
+
+ getAllSessions(): (Omit & { sessionId: string })[] {
+ const allSessions = [];
+ this.sessions.forEach((instance, sessionId) => {
+ const { client, ...sessionData } = instance;
+ allSessions.push({
+ sessionId,
+ ...sessionData
+ });
+ });
+ return allSessions;
+ }
+
+ /**
+ * 获取session的Client实例
+ */
+ getClient(sessionId: string): Client | undefined {
+ const session = this.sessions.get(sessionId);
+ return session?.client;
+ }
+
+ /**
+ * 删除session
+ */
+ async removeSession(sessionId: string): Promise {
+ const session = this.sessions.get(sessionId);
+ if (!session) {
+ log.warn(`Session ${sessionId} not found in memory, checking filesystem...`);
+ // 即使内存中没有会话,也尝试清理文件系统
+ const cleanupSuccess = await this.forceCleanupSessionFiles(sessionId);
+ return cleanupSuccess;
+ }
+
+ log.info(`Attempting to remove session: ${sessionId}`);
+
+ // 1. Immediately remove the session from the map to prevent race conditions.
+ // This is now the single source of truth for the session's existence.
+ this.sessions.delete(sessionId);
+ log.info(`Session ${sessionId} removed from in-memory map.`);
+
+ // 2. Clean up any pending QR code promises for this session.
+ this.qrPromises.delete(sessionId);
+
+ let clientKillSuccess = true;
+ try {
+ // 3. Attempt to gracefully kill the client process.
+ if (session.client) {
+ await session.client.kill('SESSION_REMOVED');
+ log.info(`Client for session ${sessionId} killed.`);
+ // 等待一段时间让进程完全退出和文件句柄释放
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ }
+ } catch (error) {
+ log.error(`Error during client kill for session ${sessionId}, continuing cleanup:`, error);
+ clientKillSuccess = false;
+ // 即使杀死进程失败,也给一些时间让进程自然结束
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ }
+
+ // 4. 强制清理文件系统
+ const filesystemCleanupSuccess = await this.forceCleanupSessionFiles(sessionId);
+
+ const overallSuccess = clientKillSuccess && filesystemCleanupSuccess;
+ log.info(`Session ${sessionId} removal process completed. Success: ${overallSuccess}`);
+ return overallSuccess;
+ }
+
+ /**
+ * 强制清理会话文件,包含重试机制
+ */
+ private async forceCleanupSessionFiles(sessionId: string): Promise {
+ const sessionDir = path.resolve('./sessions', sessionId);
+
+ if (!fs.existsSync(sessionDir)) {
+ log.info(`Session directory ${sessionDir} does not exist, cleanup not needed.`);
+ return true;
+ }
+
+ log.info(`Attempting to remove session directory: ${sessionDir}`);
+
+ // 重试删除,最多3次
+ for (let attempt = 1; attempt <= 3; attempt++) {
+ try {
+ // 如果是Windows系统,先尝试修改权限
+ if (process.platform === 'win32') {
+ try {
+ // 递归设置文件夹权限为可写
+ await this.setDirectoryPermissions(sessionDir);
+ } catch (permError) {
+ log.warn(`Could not set permissions for ${sessionDir}:`, permError.message);
+ }
+ }
+
+ await fs.remove(sessionDir);
+ log.info(`Session directory ${sessionDir} successfully removed on attempt ${attempt}.`);
+ return true;
+
+ } catch (error) {
+ log.error(`Attempt ${attempt} to remove session directory failed:`, error.message);
+
+ if (attempt < 3) {
+ // 等待时间逐渐增加
+ const waitTime = attempt * 1000;
+ log.info(`Waiting ${waitTime}ms before retry...`);
+ await new Promise(resolve => setTimeout(resolve, waitTime));
+ } else {
+ log.error(`Failed to remove session directory ${sessionDir} after ${attempt} attempts. Manual cleanup may be required.`);
+
+ // 最后尝试:至少删除配置文件,这样重启时不会恢复会话
+ try {
+ const configPath = path.join(sessionDir, 'config.json');
+ if (fs.existsSync(configPath)) {
+ await fs.unlink(configPath);
+ log.info(`Removed config file ${configPath} to prevent session resurrection.`);
+ }
+ } catch (configError) {
+ log.error(`Failed to remove config file:`, configError.message);
+ }
+
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 设置目录权限(Windows系统)
+ */
+ private async setDirectoryPermissions(dirPath: string): Promise {
+ if (process.platform !== 'win32') return;
+
+ try {
+ // 递归遍历目录并设置权限
+ const items = await fs.readdir(dirPath);
+
+ for (const item of items) {
+ const itemPath = path.join(dirPath, item);
+ const stats = await fs.stat(itemPath);
+
+ if (stats.isDirectory()) {
+ await this.setDirectoryPermissions(itemPath);
+ } else {
+ // 尝试设置文件为可写
+ try {
+ await fs.chmod(itemPath, 0o666);
+ } catch (chmodError) {
+ // 忽略权限设置错误
+ }
+ }
+ }
+
+ // 设置目录权限
+ await fs.chmod(dirPath, 0o777);
+ } catch (error) {
+ // 权限设置失败不是致命错误
+ throw error;
+ }
+ }
+
+ /**
+ * 检查session是否存在
+ */
+ hasSession(sessionId: string): boolean {
+ return this.sessions.has(sessionId);
+ }
+
+ /**
+ * 获取session数量
+ */
+ getSessionCount(): number {
+ return this.sessions.size;
+ }
+
+ /**
+ * 更新session活动时间
+ */
+ updateSessionActivity(sessionId: string): void {
+ const session = this.sessions.get(sessionId);
+ if (session) {
+ session.lastActivity = new Date();
+ }
+ }
+
+ /**
+ * 真正重启会话 - 保持会话数据,只重新拉取二维码
+ * @param sessionId 会话ID
+ * @param waitForQR 是否等待新的QR码
+ */
+ public async restartSession(sessionId: string, waitForQR: boolean = true): Promise<{
+ success: boolean;
+ message: string;
+ qrCode?: string;
+ }> {
+ const session = this.sessions.get(sessionId);
+ if (!session) {
+ throw new Error(`Session ${sessionId} not found`);
+ }
+
+ log.info(`真正重启会话: ${sessionId}`);
+
+ try {
+ // 1. 保存当前配置
+ const currentConfig = { ...session.config };
+
+ // 2. 如果有客户端,优雅地关闭它但不删除会话数据
+ if (session.client) {
+ log.info(`关闭会话 ${sessionId} 的客户端连接...`);
+ await session.client.kill('SESSION_RESTART');
+ // 等待客户端完全关闭
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ }
+
+ // 3. 重置会话状态但保留基本信息
+ session.client = null;
+ session.status = 'initializing';
+ session.lastActivity = new Date();
+ delete session.qrCode;
+ delete session.qrCodeUrl;
+
+ log.info(`会话 ${sessionId} 状态已重置,开始重新初始化...`);
+
+ // 4. 如果需要等待QR码,设置Promise
+ let qrCodePromise: Promise | null = null;
+ if (waitForQR) {
+ qrCodePromise = this.waitForQRCode(sessionId, 35000);
+ }
+
+ // 5. 重新创建客户端(使用相同的sessionId和配置)
+ create(currentConfig)
+ .then(client => {
+ log.info(`会话 ${sessionId} 客户端重新创建成功`);
+ session.client = client;
+ session.status = 'ready';
+ session.lastActivity = new Date();
+ this.setupSessionEvents(sessionId, client);
+ })
+ .catch(error => {
+ log.error(`会话 ${sessionId} 重启失败:`, error);
+ session.status = 'failed';
+
+ // 如果有等待的Promise,则拒绝它
+ const promiseHandlers = this.qrPromises.get(sessionId);
+ if (promiseHandlers) {
+ promiseHandlers.reject(error);
+ this.qrPromises.delete(sessionId);
+ }
+ });
+
+ // 6. 如果需要,等待QR码
+ if (waitForQR && qrCodePromise) {
+ log.info(`等待会话 ${sessionId} 的新QR码...`);
+ try {
+ const qrCode = await qrCodePromise;
+ if (qrCode) {
+ log.info(`会话 ${sessionId} 重启成功,获得新QR码`);
+ return {
+ success: true,
+ message: "会话重启成功,请扫描新的二维码",
+ qrCode,
+ };
+ } else {
+ log.warn(`会话 ${sessionId} 重启超时`);
+ return {
+ success: false,
+ message: '重启超时,未能获取新的二维码',
+ };
+ }
+ } catch(error) {
+ log.error(`会话 ${sessionId} 重启过程中出错:`, error);
+ return {
+ success: false,
+ message: `重启失败: ${error.message}`,
+ };
+ }
+ } else {
+ return {
+ success: true,
+ message: '会话重启已启动,请稍后获取二维码',
+ };
+ }
+
+ } catch (error) {
+ log.error(`重启会话 ${sessionId} 时发生错误:`, error);
+ session.status = 'failed';
+ throw error;
+ }
+ }
+
+ /**
+ * 更新并保存会话的代理配置,然后重启会话
+ * @param sessionId 会话ID
+ * @param proxyConfig 新的代理配置
+ */
+ public async updateSessionProxy(sessionId: string, proxyConfig: any): Promise {
+ const session = this.sessions.get(sessionId);
+ if (!session) {
+ throw new Error(`Session ${sessionId} not found`);
+ }
+
+ const sessionDir = path.join('./sessions', sessionId);
+ if (!fs.existsSync(sessionDir)) {
+ fs.mkdirSync(sessionDir, { recursive: true });
+ }
+
+ const configPath = path.join(sessionDir, 'config.json');
+ let config: Partial = {};
+ if (fs.existsSync(configPath)) {
+ try {
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
+ } catch (e) {
+ log.error(`Error reading config file for ${sessionId}, a new one will be created.`);
+ }
+ }
+
+ // 更新代理配置
+ config.proxyServerCredentials = proxyConfig;
+ // 同步useNativeProxy状态
+ if (proxyConfig && proxyConfig.address) {
+ config.useNativeProxy = true;
+ } else {
+ delete config.useNativeProxy;
+ delete config.proxyServerCredentials;
+ }
+
+ // 保存更新后的配置
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 4));
+ log.info(`Proxy config for ${sessionId} saved to ${configPath}:`, {
+ proxyAddress: config.proxyServerCredentials?.address,
+ useNativeProxy: config.useNativeProxy
+ });
+
+ // 立即更新内存中的配置
+ session.config = { ...session.config, ...config };
+ session.lastActivity = new Date();
+
+ log.info(`Memory config updated for ${sessionId}:`, {
+ proxyAddress: session.config.proxyServerCredentials?.address,
+ useNativeProxy: session.config.useNativeProxy
+ });
+
+ // 使用真正的重启功能
+ log.info(`重启会话 ${sessionId} 以应用新的代理设置...`);
+ await this.restartSession(sessionId, false);
+ log.info(`会话 ${sessionId} 重启成功,代理配置已应用`);
+
+ return true;
+ }
+
+ /**
+ * 设置session事件监听
+ */
+ private setupSessionEvents(sessionId: string, client: Client): void {
+ // 监听session断开连接
+ client.onLogout(() => {
+ log.info(`Session ${sessionId} logged out`);
+ const session = this.sessions.get(sessionId);
+ if (session) {
+ session.status = 'terminated';
+ }
+ });
+
+ // 监听其他重要事件
+ client.onMessage(() => {
+ this.updateSessionActivity(sessionId);
+ });
+ }
+
+ /**
+ * 生成唯一端口号
+ */
+ private generateUniquePort(): number {
+ const usedPorts = Array.from(this.sessions.values())
+ .map(session => session.config.port)
+ .filter(port => port !== undefined);
+
+ let port = 8080;
+ while (usedPorts.includes(port)) {
+ port++;
+ }
+ return port;
+ }
+
+ /**
+ * 清理所有session
+ */
+ async cleanup(): Promise {
+ log.info('Cleaning up all sessions...');
+
+ // 清理所有QR码Promise
+ this.qrPromises.clear();
+
+ const cleanupPromises = Array.from(this.sessions.keys()).map(sessionId =>
+ this.removeSession(sessionId)
+ );
+ await Promise.all(cleanupPromises);
+ log.info('All sessions cleaned up');
+ }
+}
+
+// 全局session管理器实例
+export const globalSessionManager = new SessionManager();
\ No newline at end of file
diff --git a/src/controllers/auth.js b/src/controllers/auth.js
new file mode 100644
index 0000000000..3f58c6e2a5
--- /dev/null
+++ b/src/controllers/auth.js
@@ -0,0 +1,465 @@
+"use strict";
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.QRManager = exports.phoneIsOutOfReach = exports.sessionDataInvalid = exports.waitForRipeSession = exports.needsToScan = exports.isAuthenticated = void 0;
+var qrcode = require("qrcode-terminal");
+var rxjs_1 = require("rxjs");
+var events_1 = require("./events");
+var initializer_1 = require("./initializer");
+var tools_1 = require("../utils/tools");
+var browser_1 = require("./browser");
+var axios_1 = require("axios");
+var logging_1 = require("../logging/logging");
+var boxen_1 = require("boxen");
+/**
+ * isAuthenticated
+ * Validates if client is authenticated
+ * @returns true if is authenticated, false otherwise
+ * @param waPage
+ */
+var isAuthenticated = function (waPage) { return (0, rxjs_1.race)((0, exports.needsToScan)(waPage), isInsideChat(waPage), (0, exports.sessionDataInvalid)(waPage)).toPromise(); };
+exports.isAuthenticated = isAuthenticated;
+var needsToScan = function (waPage) {
+ return (0, rxjs_1.from)(new Promise(function (resolve) { return __awaiter(void 0, void 0, void 0, function () {
+ var elementResult, error_1;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _a.trys.push([0, 2, , 3]);
+ return [4 /*yield*/, Promise.race([
+ waPage.waitForSelector("canvas[aria-label='Scan this QR code to link a device!']", { timeout: 0 })["catch"](function () { }),
+ waPage.waitForSelector("canvas[aria-label]", { timeout: 0 })["catch"](function () { })
+ ])];
+ case 1:
+ elementResult = _a.sent();
+ logging_1.log.info("🚀 ~ needsToScan ~ elementResult:", elementResult);
+ resolve(false);
+ return [3 /*break*/, 3];
+ case 2:
+ error_1 = _a.sent();
+ console.log("needsToScan -> error", error_1);
+ logging_1.log.error("needsToScan -> error", error_1);
+ return [3 /*break*/, 3];
+ case 3: return [2 /*return*/];
+ }
+ });
+ }); }));
+};
+exports.needsToScan = needsToScan;
+var isInsideChat = function (waPage) {
+ return (0, rxjs_1.from)(waPage
+ .waitForFunction("!!window.WA_AUTHENTICATED || (document.getElementsByClassName('app')[0] && document.getElementsByClassName('app')[0].attributes && !!document.getElementsByClassName('app')[0].attributes.tabindex) || (document.getElementsByClassName('two')[0] && document.getElementsByClassName('two')[0].attributes && !!document.getElementsByClassName('two')[0].attributes.tabindex)", { timeout: 0 })
+ .then(function () { return true; }));
+};
+var isTosBlocked = function (waPage) {
+ return (0, rxjs_1.from)(waPage
+ .waitForFunction("document.getElementsByTagName(\"html\")[0].classList[0] === 'no-js'", { timeout: 0 })
+ .then(function () { return false; }));
+};
+var waitForRipeSession = function (waPage, waitForRipeSessionTimeout) { return __awaiter(void 0, void 0, void 0, function () {
+ var error_2;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _a.trys.push([0, 2, , 3]);
+ return [4 /*yield*/, waPage.waitForFunction("window.isRipeSession()", { timeout: (waitForRipeSessionTimeout !== null && waitForRipeSessionTimeout !== void 0 ? waitForRipeSessionTimeout : 5) * 1000, polling: 1000 })];
+ case 1:
+ _a.sent();
+ return [2 /*return*/, true];
+ case 2:
+ error_2 = _a.sent();
+ return [2 /*return*/, false];
+ case 3: return [2 /*return*/];
+ }
+ });
+}); };
+exports.waitForRipeSession = waitForRipeSession;
+var sessionDataInvalid = function (waPage) { return __awaiter(void 0, void 0, void 0, function () {
+ var check;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ check = "Object.keys(localStorage).includes(\"old-logout-cred\")";
+ return [4 /*yield*/, waPage
+ .waitForFunction(check, { timeout: 0, polling: 'mutation' })
+ // await injectApi(waPage, null, true);
+ // await waPage
+ // .waitForFunction(
+ // '!window.getQrPng',
+ // { timeout: 0, polling: 'mutation' }
+ // )
+ // await timeout(1000000)
+ //NEED A DIFFERENT WAY TO DETERMINE IF THE SESSION WAS LOGGED OUT!!!!
+ //if the code reaches here it means the browser was refreshed. Nuke the session data and restart `create`
+ ];
+ case 1:
+ _a.sent();
+ // await injectApi(waPage, null, true);
+ // await waPage
+ // .waitForFunction(
+ // '!window.getQrPng',
+ // { timeout: 0, polling: 'mutation' }
+ // )
+ // await timeout(1000000)
+ //NEED A DIFFERENT WAY TO DETERMINE IF THE SESSION WAS LOGGED OUT!!!!
+ //if the code reaches here it means the browser was refreshed. Nuke the session data and restart `create`
+ return [2 /*return*/, 'NUKE'];
+ }
+ });
+}); };
+exports.sessionDataInvalid = sessionDataInvalid;
+var phoneIsOutOfReach = function (waPage) { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, waPage
+ .waitForFunction('document.querySelector("body").innerText.includes("Trying to reach phone")', { timeout: 0, polling: 'mutation' })
+ .then(function () { return true; })["catch"](function () { return false; })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+}); };
+exports.phoneIsOutOfReach = phoneIsOutOfReach;
+var QRManager = /** @class */ (function () {
+ function QRManager(config) {
+ if (config === void 0) { config = null; }
+ this.qrEv = null;
+ this.qrNum = 0;
+ this.hash = 'START';
+ this.config = null;
+ this.firstEmitted = false;
+ this._internalQrPngLoaded = false;
+ this.qrCheck = "document.querySelector(\"canvas[aria-label]\")?document.querySelector(\"canvas[aria-label]\").parentElement.getAttribute(\"data-ref\"):false";
+ this.config = config;
+ this.setConfig(this.config);
+ }
+ QRManager.prototype.setConfig = function (config) {
+ this.config = config;
+ this.qrEvF(this.config);
+ };
+ QRManager.prototype.qrEvF = function (config) {
+ if (config === void 0) { config = this.config; }
+ // return new EvEmitter(config.sessionId || 'session', 'qr');
+ if (!this.qrEv)
+ this.qrEv = new events_1.EvEmitter(config.sessionId || 'session', 'qr');
+ return this.qrEv;
+ };
+ QRManager.prototype.grabAndEmit = function (qrData, waPage, config, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var isLinkCode, qrEv, t, qrPng, _a, host_1, error_3, lr;
+ var _this = this;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ isLinkCode = qrData.length === 9;
+ this.qrNum++;
+ if (!(config.qrMax && this.qrNum > config.qrMax)) return [3 /*break*/, 2];
+ spinner.info('QR Code limit reached, exiting...');
+ return [4 /*yield*/, (0, browser_1.kill)(waPage, null, true, null, "QR_LIMIT_REACHED")];
+ case 1:
+ _b.sent();
+ _b.label = 2;
+ case 2:
+ qrEv = this.qrEvF(config);
+ if ((!this.qrNum || this.qrNum == 1) && browser_1.BROWSER_START_TS)
+ spinner.info("First QR: ".concat(Date.now() - browser_1.BROWSER_START_TS, " ms"));
+ if (qrData) {
+ qrEv.emit(qrData, "qrData");
+ if (!config.qrLogSkip) {
+ if (isLinkCode) {
+ console.log((0, boxen_1["default"])(qrData, { title: "ENTER THIS CODE ON THE HOST ACCOUNT DEVICE: ".concat(config.sessionId), padding: 1, titleAlignment: 'center' }));
+ }
+ else {
+ qrcode.generate(qrData, { small: true }, function (terminalQrCode) {
+ console.log((0, boxen_1["default"])(terminalQrCode, { title: config.sessionId, padding: 1, titleAlignment: 'center' }));
+ });
+ }
+ }
+ else {
+ console.log("New QR Code generated. Not printing in console because qrLogSkip is set to true");
+ logging_1.log.info("New QR Code generated. Not printing in console because qrLogSkip is set to true");
+ }
+ }
+ if (!!this._internalQrPngLoaded) return [3 /*break*/, 4];
+ logging_1.log.info("Waiting for internal QR renderer to load");
+ return [4 /*yield*/, (0, tools_1.timePromise)(function () { return waPage.waitForFunction("window.getQrPng || false", { timeout: 0, polling: 'mutation' }); })];
+ case 3:
+ t = _b.sent();
+ logging_1.log.info("Internal QR renderer loaded in ".concat(t, " ms"));
+ this._internalQrPngLoaded = true;
+ _b.label = 4;
+ case 4:
+ _b.trys.push([4, 12, , 14]);
+ if (!isLinkCode) return [3 /*break*/, 5];
+ _a = qrData;
+ return [3 /*break*/, 7];
+ case 5: return [4 /*yield*/, waPage.evaluate("window.getQrPng()")];
+ case 6:
+ _a = _b.sent();
+ _b.label = 7;
+ case 7:
+ qrPng = _a;
+ if (!qrPng) return [3 /*break*/, 10];
+ qrEv.emit(qrPng);
+ (0, tools_1.processSend)('ready');
+ if (!config.ezqr) return [3 /*break*/, 9];
+ host_1 = 'https://qr.openwa.cloud/';
+ return [4 /*yield*/, axios_1["default"].post(host_1, {
+ value: qrPng,
+ hash: this.hash
+ }).then(function (_a) {
+ var data = _a.data;
+ if (_this.hash === 'START') {
+ var qrUrl = "".concat(host_1).concat(data);
+ qrEv.emit(qrUrl, "qrUrl");
+ console.log("Scan the qr code at ".concat(qrUrl));
+ logging_1.log.info("Scan the qr code at ".concat(qrUrl));
+ }
+ _this.hash = data;
+ })["catch"](function (e) {
+ console.error(e);
+ _this.hash = 'START';
+ })];
+ case 8:
+ _b.sent();
+ _b.label = 9;
+ case 9: return [3 /*break*/, 11];
+ case 10:
+ spinner.info("Something went wrong while retreiving new the QR code but it should not affect the session launch procedure.");
+ _b.label = 11;
+ case 11: return [3 /*break*/, 14];
+ case 12:
+ error_3 = _b.sent();
+ return [4 /*yield*/, waPage.evaluate("window.launchres")];
+ case 13:
+ lr = _b.sent();
+ console.log(lr);
+ logging_1.log.info('smartQr -> error', { lr: lr });
+ spinner.info("Something went wrong while retreiving new the QR code but it should not affect the session launch procedure: ".concat(error_3.message));
+ return [3 /*break*/, 14];
+ case 14: return [2 /*return*/];
+ }
+ });
+ });
+ };
+ QRManager.prototype.linkCode = function (waPage, config, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var evalResult, isAuthed, _hasDefaultStateYet, linkCode;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, waPage.evaluate("window.Store && window.Store.State")];
+ case 1:
+ evalResult = _a.sent();
+ if (evalResult === false) {
+ console.log('Seems as though you have been TOS_BLOCKed, unable to refresh QR Code. Please see https://github.com/open-wa/wa-automate-nodejs#best-practice for information on how to prevent this from happeing. You will most likely not get a QR Code');
+ logging_1.log.warn('Seems as though you have been TOS_BLOCKed, unable to refresh QR Code. Please see https://github.com/open-wa/wa-automate-nodejs#best-practice for information on how to prevent this from happeing. You will most likely not get a QR Code');
+ if (config.throwErrorOnTosBlock)
+ throw new Error('TOSBLOCK');
+ }
+ return [4 /*yield*/, (0, exports.isAuthenticated)(waPage)];
+ case 2:
+ isAuthed = _a.sent();
+ if (isAuthed)
+ return [2 /*return*/, true];
+ return [4 /*yield*/, waPage.evaluate("!!(window.Store && window.Store.State && window.Store.State.Socket)")];
+ case 3:
+ _hasDefaultStateYet = _a.sent();
+ if (!!_hasDefaultStateYet) return [3 /*break*/, 5];
+ //expecting issue, take a screenshot then wait a few seconds before continuing
+ return [4 /*yield*/, (0, tools_1.timeout)(2000)];
+ case 4:
+ //expecting issue, take a screenshot then wait a few seconds before continuing
+ _a.sent();
+ _a.label = 5;
+ case 5:
+ spinner.info('Link Code requested, please use the link code to login from your host account device');
+ return [4 /*yield*/, waPage.evaluate(function (number) { return window['linkCode'](number); }, config === null || config === void 0 ? void 0 : config.linkCode)];
+ case 6:
+ linkCode = _a.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("Link Code please use this to login from your host account device: ".concat(linkCode));
+ return [4 /*yield*/, this.grabAndEmit(linkCode, waPage, config, spinner)];
+ case 7:
+ _a.sent();
+ return [4 /*yield*/, isInsideChat(waPage).toPromise()];
+ case 8: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+ };
+ QRManager.prototype.smartQr = function (waPage, config, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var evalResult, isAuthed, _hasDefaultStateYet;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, waPage.evaluate("window.Store && window.Store.State")];
+ case 1:
+ evalResult = _a.sent();
+ if (evalResult === false) {
+ console.log('Seems as though you have been TOS_BLOCKed, unable to refresh QR Code. Please see https://github.com/open-wa/wa-automate-nodejs#best-practice for information on how to prevent this from happeing. You will most likely not get a QR Code');
+ logging_1.log.warn('Seems as though you have been TOS_BLOCKed, unable to refresh QR Code. Please see https://github.com/open-wa/wa-automate-nodejs#best-practice for information on how to prevent this from happeing. You will most likely not get a QR Code');
+ if (config.throwErrorOnTosBlock)
+ throw new Error('TOSBLOCK');
+ }
+ return [4 /*yield*/, (0, exports.isAuthenticated)(waPage)];
+ case 2:
+ isAuthed = _a.sent();
+ if (isAuthed)
+ return [2 /*return*/, true];
+ return [4 /*yield*/, waPage.evaluate("!!(window.Store && window.Store.State && window.Store.State.Socket)")];
+ case 3:
+ _hasDefaultStateYet = _a.sent();
+ if (!!_hasDefaultStateYet) return [3 /*break*/, 5];
+ //expecting issue, take a screenshot then wait a few seconds before continuing
+ return [4 /*yield*/, (0, tools_1.timeout)(2000)];
+ case 4:
+ //expecting issue, take a screenshot then wait a few seconds before continuing
+ _a.sent();
+ _a.label = 5;
+ case 5: return [2 /*return*/, new Promise(function (resolve) { return __awaiter(_this, void 0, void 0, function () {
+ var funcName, md, gotResult, fn, set;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ funcName = '_smartQr';
+ md = "MULTI_DEVICE_DETECTED";
+ gotResult = false;
+ fn = function (qrData) { return __awaiter(_this, void 0, void 0, function () {
+ var _a;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (qrData.length > 200 && !(config === null || config === void 0 ? void 0 : config.multiDevice)) {
+ spinner.fail("Multi-Device detected, please set multiDevice to true in your config or add the --multi-device flag");
+ spinner.emit(true, "MD_DETECT");
+ return [2 /*return*/, resolve(md)];
+ }
+ if (!(!gotResult && (qrData === 'QR_CODE_SUCCESS' || qrData === md))) return [3 /*break*/, 2];
+ gotResult = true;
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("QR code scanned. Loading session...");
+ _a = resolve;
+ return [4 /*yield*/, isInsideChat(waPage).toPromise()];
+ case 1: return [2 /*return*/, _a.apply(void 0, [_b.sent()])];
+ case 2:
+ if (!gotResult)
+ this.grabAndEmit(qrData, waPage, config, spinner);
+ return [2 /*return*/];
+ }
+ });
+ }); };
+ set = function () { return waPage.evaluate(function (_a) {
+ var funcName = _a.funcName;
+ //@ts-ignore
+ return window['smartQr'] ? window["smartQr"](function (obj) { return window[funcName](obj); }) : false;
+ }, { funcName: funcName }); };
+ return [4 /*yield*/, waPage.exposeFunction(funcName, function (obj) { return fn(obj); }).then(set)["catch"](function (e) { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ //if an error occurs during the qr launcher then take a screenshot.
+ return [4 /*yield*/, (0, initializer_1.screenshot)(waPage)];
+ case 1:
+ //if an error occurs during the qr launcher then take a screenshot.
+ _a.sent();
+ console.log("qr -> e", e);
+ logging_1.log.error("qr -> e", e);
+ return [2 /*return*/];
+ }
+ });
+ }); })];
+ case 1:
+ _a.sent();
+ return [4 /*yield*/, this.emitFirst(waPage, config, spinner)];
+ case 2:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ }); })];
+ }
+ });
+ });
+ };
+ QRManager.prototype.emitFirst = function (waPage, config, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var firstQr;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (this.firstEmitted)
+ return [2 /*return*/];
+ this.firstEmitted = true;
+ return [4 /*yield*/, waPage.evaluate(this.qrCheck)];
+ case 1:
+ firstQr = _a.sent();
+ return [4 /*yield*/, this.grabAndEmit(firstQr, waPage, config, spinner)];
+ case 2:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /**
+ * Wait 10 seconds for the qr element to show.
+ * If it doesn't show up within 10 seconds then assume the session is authed already or blocked therefore ignore and return promise
+ */
+ QRManager.prototype.waitFirstQr = function (waPage, config, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var fqr;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, waPage.waitForFunction("!!(".concat(this.qrCheck, ")"), {
+ polling: 500,
+ timeout: 10000
+ })["catch"](function () { return false; })];
+ case 1:
+ fqr = _a.sent();
+ if (!fqr) return [3 /*break*/, 3];
+ return [4 /*yield*/, this.emitFirst(waPage, config, spinner)];
+ case 2:
+ _a.sent();
+ _a.label = 3;
+ case 3: return [2 /*return*/];
+ }
+ });
+ });
+ };
+ return QRManager;
+}());
+exports.QRManager = QRManager;
diff --git a/src/controllers/browser.js b/src/controllers/browser.js
new file mode 100644
index 0000000000..712ed70767
--- /dev/null
+++ b/src/controllers/browser.js
@@ -0,0 +1,961 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+ if (ar || !(i in from)) {
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+ ar[i] = from[i];
+ }
+ }
+ return to.concat(ar || Array.prototype.slice.call(from));
+};
+exports.__esModule = true;
+exports.kill = exports.injectApi = exports.injectWapi = exports.injectPreApiScripts = exports.addScript = exports.getSessionDataFilePath = exports.invalidateSesssionData = exports.deleteSessionData = exports.initPage = exports.BROWSER_START_TS = void 0;
+var path = require("path");
+var fs = require("fs/promises");
+var readline = require("readline");
+var death_1 = require("death");
+// import puppeteer from 'puppeteer-extra';
+var puppeteer_config_1 = require("../config/puppeteer.config");
+var puppeteer_1 = require("puppeteer");
+var events_1 = require("./events");
+var pico_s3_1 = require("pico-s3");
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+var puppeteer = require('puppeteer-extra');
+var promise_1 = require("terminate/promise");
+var logging_1 = require("../logging/logging");
+var tools_1 = require("../utils/tools");
+var script_preloader_1 = require("./script_preloader");
+var patch_manager_1 = require("./patch_manager");
+var init_patch_1 = require("./init_patch");
+var browser, wapiInjected = false, pageCache = undefined, wapiAttempts = 1;
+exports.BROWSER_START_TS = 0;
+function initPage(sessionId, config, qrManager, customUserAgent, spinner, _page, skipAuth) {
+ var _a, _b, _c, _d;
+ return __awaiter(this, void 0, void 0, function () {
+ var setupPromises, stealth, waPage, startBrowser, postBrowserLaunchTs, cacheEnabled, blockCrashLogs, blockAssets, block, interceptAuthentication, proxyAddr, quickAuthed, proxy, localPageCacheExists, authCompleteEv_1, sessionjson, _e, _f, _g, _h, error_1, WEB_START_TS, webRes, WEB_END_TS, error_2;
+ var _this = this;
+ return __generator(this, function (_j) {
+ switch (_j.label) {
+ case 0:
+ setupPromises = [];
+ return [4 /*yield*/, script_preloader_1.scriptLoader.loadScripts()];
+ case 1:
+ _j.sent();
+ if ((config === null || config === void 0 ? void 0 : config.resizable) === undefined || !(config === null || config === void 0 ? void 0 : config.resizable) == false)
+ config.defaultViewport = null;
+ if (!(config === null || config === void 0 ? void 0 : config.useStealth)) return [3 /*break*/, 3];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('puppeteer-extra-plugin-stealth'); })];
+ case 2:
+ stealth = (_j.sent())["default"];
+ puppeteer.use(stealth());
+ _j.label = 3;
+ case 3:
+ waPage = _page;
+ if (!!waPage) return [3 /*break*/, 6];
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Launching Browser');
+ startBrowser = (0, tools_1.now)();
+ return [4 /*yield*/, initBrowser(sessionId, config, spinner)];
+ case 4:
+ browser = _j.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Browser launched: ".concat(((0, tools_1.now)() - startBrowser).toFixed(0), "ms"));
+ return [4 /*yield*/, getWAPage(browser)];
+ case 5:
+ waPage = _j.sent();
+ if (process.stdin.setRawMode && typeof process.stdin.setRawMode == "function") {
+ readline.emitKeypressEvents(process.stdin);
+ process.stdin.setRawMode(true);
+ console.log('Press "s" to take a screenshot. Press "Ctrl+C" to quit.');
+ process.stdin.on('keypress', function (str, key) { return __awaiter(_this, void 0, void 0, function () {
+ var path_1;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!(key.name === 's')) return [3 /*break*/, 2];
+ path_1 = "./screenshot_".concat(config.sessionId || "session", "_").concat(Date.now(), ".png");
+ console.log("Taking screenshot: ".concat(path_1, " ..."));
+ return [4 /*yield*/, waPage.screenshot({ path: path_1 })];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2:
+ if (key.ctrl && key.name === 'c') {
+ process.exit();
+ }
+ return [2 /*return*/];
+ }
+ });
+ }); });
+ }
+ else
+ console.log("Unable to set raw mode on stdin. Keypress events will not be emitted. You cannot take a screenshot by pressing 's' during launch.");
+ _j.label = 6;
+ case 6:
+ //@ts-ignore
+ return [4 /*yield*/, (typeof waPage._client === 'function' && waPage._client() || waPage._client).send('Network.setBypassServiceWorker', { bypass: true })];
+ case 7:
+ //@ts-ignore
+ _j.sent();
+ postBrowserLaunchTs = (0, tools_1.now)();
+ waPage.on("framenavigated", function (frame) { return __awaiter(_this, void 0, void 0, function () {
+ var frameNavPromises, hasWapi, error_3;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _a.trys.push([0, 3, , 4]);
+ frameNavPromises = [];
+ // const content = await frame.content() //not using content right now so save some ms by commenting out
+ logging_1.log.info("FRAME NAV DETECTED, ".concat(frame.url(), ", Reinjecting APIs..."));
+ return [4 /*yield*/, waPage.evaluate("window.WAPI ? true : false")];
+ case 1:
+ hasWapi = _a.sent();
+ if (!hasWapi) {
+ logging_1.log.info("FN: WAPI missing. Reinjecting APIs...");
+ frameNavPromises.push(injectApi(waPage, spinner, true));
+ frameNavPromises.push(qrManager.waitFirstQr(waPage, config, spinner));
+ }
+ else
+ logging_1.log.info("FN: WAPI intact. Skipping reinjection...");
+ if (frame.url().includes('post_logout=1')) {
+ console.log("Session most likely logged out");
+ }
+ return [4 /*yield*/, Promise.all(frameNavPromises)];
+ case 2:
+ _a.sent();
+ return [3 /*break*/, 4];
+ case 3:
+ error_3 = _a.sent();
+ logging_1.log.error('framenaverr', error_3);
+ return [3 /*break*/, 4];
+ case 4: return [2 /*return*/];
+ }
+ });
+ }); });
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Setting Up Page');
+ if (!(config === null || config === void 0 ? void 0 : config.proxyServerCredentials)) return [3 /*break*/, 9];
+ return [4 /*yield*/, waPage.authenticate(config.proxyServerCredentials)];
+ case 8:
+ _j.sent();
+ _j.label = 9;
+ case 9:
+ setupPromises.push(waPage.setUserAgent(customUserAgent || puppeteer_config_1.useragent));
+ if ((config === null || config === void 0 ? void 0 : config.defaultViewport) !== null)
+ setupPromises.push(waPage.setViewport({
+ width: ((_a = config === null || config === void 0 ? void 0 : config.viewport) === null || _a === void 0 ? void 0 : _a.width) || puppeteer_config_1.width,
+ height: ((_b = config === null || config === void 0 ? void 0 : config.viewport) === null || _b === void 0 ? void 0 : _b.height) || puppeteer_config_1.height,
+ deviceScaleFactor: 1
+ }));
+ cacheEnabled = (config === null || config === void 0 ? void 0 : config.cacheEnabled) === false ? false : true;
+ blockCrashLogs = (config === null || config === void 0 ? void 0 : config.blockCrashLogs) === false ? false : true;
+ setupPromises.push(waPage.setBypassCSP((config === null || config === void 0 ? void 0 : config.bypassCSP) || false));
+ setupPromises.push(waPage.setCacheEnabled(cacheEnabled));
+ blockAssets = !(config === null || config === void 0 ? void 0 : config.headless) ? false : (config === null || config === void 0 ? void 0 : config.blockAssets) || false;
+ if (!blockAssets) return [3 /*break*/, 11];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('puppeteer-extra-plugin-block-resources'); })];
+ case 10:
+ block = (_j.sent())["default"];
+ puppeteer.use(block({
+ blockedTypes: new Set(['image', 'stylesheet', 'font'])
+ }));
+ _j.label = 11;
+ case 11:
+ interceptAuthentication = !(config === null || config === void 0 ? void 0 : config.safeMode);
+ proxyAddr = (config === null || config === void 0 ? void 0 : config.proxyServerCredentials) ? "".concat(((_c = config.proxyServerCredentials) === null || _c === void 0 ? void 0 : _c.protocol) ||
+ config.proxyServerCredentials.address.includes('https') ? 'https' :
+ config.proxyServerCredentials.address.includes('http') ? 'http' :
+ config.proxyServerCredentials.address.includes('socks5h') ? 'socks5h' :
+ config.proxyServerCredentials.address.includes('socks5') ? 'socks5' :
+ config.proxyServerCredentials.address.includes('socks4') ? 'socks4' : 'http', "://").concat(config.proxyServerCredentials.username, ":").concat(config.proxyServerCredentials.password, "@").concat(config.proxyServerCredentials.address
+ .replace('https', '')
+ .replace('http', '')
+ .replace('socks5h', '')
+ .replace('socks5', '')
+ .replace('socks4', '')
+ .replace('://', '')) : false;
+ quickAuthed = false;
+ if (!proxyAddr) return [3 /*break*/, 13];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('smashah-puppeteer-page-proxy'); })];
+ case 12:
+ proxy = (_j.sent())["default"];
+ _j.label = 13;
+ case 13:
+ if (!process.env.WA_LOCAL_PAGE_CACHE) return [3 /*break*/, 16];
+ return [4 /*yield*/, (0, tools_1.pathExists)(process.env.WA_LOCAL_PAGE_CACHE, true)];
+ case 14:
+ localPageCacheExists = _j.sent();
+ logging_1.log.info("Local page cache env var set: ".concat(process.env.WA_LOCAL_PAGE_CACHE, " ").concat(localPageCacheExists));
+ if (!localPageCacheExists) return [3 /*break*/, 16];
+ logging_1.log.info("Local page cache file exists. Loading...");
+ return [4 /*yield*/, fs.readFile(process.env.WA_LOCAL_PAGE_CACHE, "utf8")];
+ case 15:
+ pageCache = _j.sent();
+ _j.label = 16;
+ case 16:
+ if (!(interceptAuthentication || proxyAddr || blockCrashLogs || true)) return [3 /*break*/, 18];
+ return [4 /*yield*/, waPage.setRequestInterception(true)];
+ case 17:
+ _j.sent();
+ waPage.on('response', function (response) { return __awaiter(_this, void 0, void 0, function () {
+ var t, error_4;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _a.trys.push([0, 4, , 5]);
+ if (!(response.request().url() == "https://web.whatsapp.com/")) return [3 /*break*/, 3];
+ return [4 /*yield*/, response.text()];
+ case 1:
+ t = _a.sent();
+ if (!(t.includes("class=\"no-js\"") && t.includes("self.") && !pageCache)) return [3 /*break*/, 3];
+ //this is a valid response, save it for later
+ pageCache = t;
+ logging_1.log.info("saving valid page to dumb cache");
+ if (!process.env.WA_LOCAL_PAGE_CACHE) return [3 /*break*/, 3];
+ logging_1.log.info("Writing page cache to local file: ".concat(process.env.WA_LOCAL_PAGE_CACHE));
+ return [4 /*yield*/, fs.writeFile(process.env.WA_LOCAL_PAGE_CACHE, pageCache)];
+ case 2:
+ _a.sent();
+ _a.label = 3;
+ case 3: return [3 /*break*/, 5];
+ case 4:
+ error_4 = _a.sent();
+ logging_1.log.error("page cache error", error_4);
+ return [3 /*break*/, 5];
+ case 5: return [2 /*return*/];
+ }
+ });
+ }); });
+ authCompleteEv_1 = new events_1.EvEmitter(sessionId, 'AUTH');
+ waPage.on('request', function (request) { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!(request.url() === "https://web.whatsapp.com/" && pageCache && !proxyAddr)) return [3 /*break*/, 2];
+ //if the pageCache isn't set and this response includes
+ logging_1.log.info("reviving page from page cache");
+ return [4 /*yield*/, request.respond({
+ status: 200,
+ body: pageCache
+ })];
+ case 1: return [2 /*return*/, _a.sent()];
+ case 2:
+ if (!(interceptAuthentication &&
+ request.url().includes('_priority_components') &&
+ !quickAuthed)) return [3 /*break*/, 4];
+ authCompleteEv_1.emit(true);
+ return [4 /*yield*/, waPage.evaluate('window.WA_AUTHENTICATED=true;')];
+ case 3:
+ _a.sent();
+ quickAuthed = true;
+ _a.label = 4;
+ case 4:
+ if (["https://dit.whatsapp.net/deidentified_telemetry", "https://crashlogs.whatsapp.net/"].find(function (u) { return request.url().includes(u); }) && blockCrashLogs) {
+ request.abort();
+ }
+ else if (proxyAddr && !(config === null || config === void 0 ? void 0 : config.useNativeProxy)) {
+ proxy(request, proxyAddr);
+ }
+ else
+ request["continue"]();
+ return [2 /*return*/];
+ }
+ });
+ }); });
+ _j.label = 18;
+ case 18:
+ if (!skipAuth) return [3 /*break*/, 19];
+ spinner.info("Skipping Authentication");
+ return [3 /*break*/, 29];
+ case 19:
+ /**
+ * AUTH
+ */
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Loading session data');
+ return [4 /*yield*/, getSessionDataFromFile(sessionId, config, spinner)];
+ case 20:
+ sessionjson = _j.sent();
+ if (!(!sessionjson && sessionjson !== "" && config.sessionDataBucketAuth)) return [3 /*break*/, 24];
+ _j.label = 21;
+ case 21:
+ _j.trys.push([21, 23, , 24]);
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Unable to find session data file locally, attempting to find session data in cloud storage..');
+ _f = (_e = JSON).parse;
+ _h = (_g = Buffer).from;
+ return [4 /*yield*/, (0, pico_s3_1.getTextFile)(__assign(__assign({ directory: '_sessionData' }, JSON.parse(Buffer.from(config.sessionDataBucketAuth, 'base64').toString('ascii'))), { filename: "".concat(config.sessionId || 'session', ".data.json") }))];
+ case 22:
+ sessionjson = _f.apply(_e, [_h.apply(_g, [_j.sent(), 'base64']).toString('ascii')]);
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed('Successfully downloaded session data file from cloud storage!');
+ return [3 /*break*/, 24];
+ case 23:
+ error_1 = _j.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail("".concat(error_1 instanceof pico_s3_1.FileNotFoundError ? 'The session data file was not found in the cloud storage bucket' : 'Something went wrong while fetching session data from cloud storage bucket', ". Continuing..."));
+ return [3 /*break*/, 24];
+ case 24:
+ if (!(sessionjson && Object.keys(sessionjson).length)) return [3 /*break*/, 27];
+ spinner === null || spinner === void 0 ? void 0 : spinner.info(config.multiDevice ? "multi-device enabled. Session data skipped..." : 'Existing session data detected. Injecting...');
+ if (!!(config === null || config === void 0 ? void 0 : config.multiDevice)) return [3 /*break*/, 26];
+ return [4 /*yield*/, waPage.evaluateOnNewDocument(function (session) {
+ localStorage.clear();
+ Object.keys(session).forEach(function (key) { return localStorage.setItem(key, session[key]); });
+ }, sessionjson)];
+ case 25:
+ _j.sent();
+ _j.label = 26;
+ case 26:
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed('Existing session data injected');
+ return [3 /*break*/, 29];
+ case 27:
+ if (!(config === null || config === void 0 ? void 0 : config.multiDevice)) return [3 /*break*/, 29];
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("No session data detected. Opting in for MD.");
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Make sure to keep the session alive for at least 5 minutes after scanning the QR code before trying to restart a session!!");
+ if (!(config === null || config === void 0 ? void 0 : config.legacy)) return [3 /*break*/, 29];
+ return [4 /*yield*/, waPage.evaluateOnNewDocument(function (session) {
+ localStorage.clear();
+ Object.keys(session).forEach(function (key) { return localStorage.setItem(key, session[key]); });
+ }, {
+ "md-opted-in": "true",
+ "MdUpgradeWamFlag": "true",
+ "remember-me": "true"
+ })];
+ case 28:
+ _j.sent();
+ _j.label = 29;
+ case 29:
+ if (!((config === null || config === void 0 ? void 0 : config.proxyServerCredentials) && !(config === null || config === void 0 ? void 0 : config.useNativeProxy))) return [3 /*break*/, 31];
+ return [4 /*yield*/, proxy(waPage, proxyAddr)];
+ case 30:
+ _j.sent();
+ _j.label = 31;
+ case 31:
+ if ((_d = config === null || config === void 0 ? void 0 : config.proxyServerCredentials) === null || _d === void 0 ? void 0 : _d.address)
+ spinner.succeed("Active proxy: ".concat(config.proxyServerCredentials.address));
+ return [4 /*yield*/, Promise.all(setupPromises)];
+ case 32:
+ _j.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Pre page launch setup complete: ".concat(((0, tools_1.now)() - postBrowserLaunchTs).toFixed(0), "ms"));
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Navigating to WA');
+ _j.label = 33;
+ case 33:
+ _j.trys.push([33, 38, , 39]);
+ WEB_START_TS = new Date().getTime();
+ return [4 /*yield*/, waPage.goto(puppeteer_config_1.puppeteerConfig.WAUrl)];
+ case 34:
+ webRes = _j.sent();
+ WEB_END_TS = new Date().getTime();
+ return [4 /*yield*/, waPage.exposeFunction("ProgressBarEvent", function (_a) {
+ var value = _a.value, text = _a.text;
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("".concat((value || value === 0) && "".concat(value, "%:\t"), " ").concat(text));
+ spinner === null || spinner === void 0 ? void 0 : spinner.emit({ value: value, text: text }, "internal_launch_progress");
+ })];
+ case 35:
+ _j.sent();
+ return [4 /*yield*/, waPage.exposeFunction("CriticalInternalMessage", function (_a) {
+ var value = _a.value, text = _a.text;
+ return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("".concat(text));
+ spinner === null || spinner === void 0 ? void 0 : spinner.emit({ value: value, text: text }, "critical_internal_message");
+ if (!(value === "TEMP_BAN" && !(config.killProcessOnBan === false))) return [3 /*break*/, 2];
+ return [4 /*yield*/, (0, exports.kill)(waPage, undefined, true, undefined, "TEMP_BAN")];
+ case 1:
+ _b.sent();
+ _b.label = 2;
+ case 2: return [2 /*return*/];
+ }
+ });
+ });
+ })];
+ case 36:
+ _j.sent();
+ return [4 /*yield*/, (0, init_patch_1.injectProgObserver)(waPage)];
+ case 37:
+ _j.sent();
+ if (webRes == null) {
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Page loaded but something may have gone wrong: ".concat(WEB_END_TS - WEB_START_TS, "ms"));
+ }
+ else {
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Page loaded in ".concat(WEB_END_TS - WEB_START_TS, "ms: ").concat(webRes.status()).concat(webRes.ok() ? '' : ', ' + webRes.statusText()));
+ if (!webRes.ok())
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Headers Info: ".concat(JSON.stringify(webRes.headers(), null, 2)));
+ }
+ return [3 /*break*/, 39];
+ case 38:
+ error_2 = _j.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail(error_2);
+ throw error_2;
+ case 39: return [2 /*return*/, waPage];
+ }
+ });
+ });
+}
+exports.initPage = initPage;
+var getSessionDataFromFile = function (sessionId, config, spinner) { return __awaiter(void 0, void 0, void 0, function () {
+ var sessionjsonpath, sessionjson, sd, _a, s, msg;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if ((config === null || config === void 0 ? void 0 : config.sessionData) == "NUKE")
+ return [2 /*return*/, ''
+ //check if [session].json exists in __dirname
+ ];
+ return [4 /*yield*/, (0, exports.getSessionDataFilePath)(sessionId, config)];
+ case 1:
+ sessionjsonpath = _b.sent();
+ sessionjson = '';
+ sd = process.env["".concat(sessionId.toUpperCase(), "_DATA_JSON")] ? JSON.parse(process.env["".concat(sessionId.toUpperCase(), "_DATA_JSON")]) : config === null || config === void 0 ? void 0 : config.sessionData;
+ sessionjson = (typeof sd === 'string' && sd !== "") ? JSON.parse(Buffer.from(sd, 'base64').toString('ascii')) : sd;
+ _a = sessionjsonpath && typeof sessionjsonpath == 'string';
+ if (!_a) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, tools_1.pathExists)(sessionjsonpath)];
+ case 2:
+ _a = (_b.sent());
+ _b.label = 3;
+ case 3:
+ if (!_a) return [3 /*break*/, 5];
+ spinner.succeed("Found session data file: ".concat(sessionjsonpath));
+ return [4 /*yield*/, fs.readFile(sessionjsonpath, "utf8")];
+ case 4:
+ s = _b.sent();
+ try {
+ sessionjson = JSON.parse(s);
+ }
+ catch (error) {
+ try {
+ sessionjson = JSON.parse(Buffer.from(s, 'base64').toString('ascii'));
+ }
+ catch (error) {
+ msg = "".concat(s == "LOGGED OUT" ? "The session was logged out" : "Session data json file is corrupted", ". Please re-scan the QR code.");
+ if (spinner) {
+ spinner.fail(msg);
+ }
+ else
+ console.error(msg);
+ return [2 /*return*/, false];
+ }
+ }
+ return [3 /*break*/, 6];
+ case 5:
+ spinner.succeed("No session data file found for session : ".concat(sessionId));
+ _b.label = 6;
+ case 6: return [2 /*return*/, sessionjson];
+ }
+ });
+}); };
+var deleteSessionData = function (config) { return __awaiter(void 0, void 0, void 0, function () {
+ var sessionjsonpath, _a, l, mdDir, _b, _c, _d, _e;
+ return __generator(this, function (_f) {
+ switch (_f.label) {
+ case 0: return [4 /*yield*/, (0, exports.getSessionDataFilePath)((config === null || config === void 0 ? void 0 : config.sessionId) || 'session', config)];
+ case 1:
+ sessionjsonpath = _f.sent();
+ _a = typeof sessionjsonpath == 'string';
+ if (!_a) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, tools_1.pathExists)(sessionjsonpath)];
+ case 2:
+ _a = (_f.sent());
+ _f.label = 3;
+ case 3:
+ if (!_a) return [3 /*break*/, 5];
+ l = "logout detected, deleting session data file: ".concat(sessionjsonpath);
+ console.log(l);
+ logging_1.log.info(l);
+ return [4 /*yield*/, fs.unlink(sessionjsonpath)];
+ case 4:
+ _f.sent();
+ _f.label = 5;
+ case 5: return [4 /*yield*/, (0, tools_1.pathExists)(config['userDataDir'])];
+ case 6:
+ mdDir = _f.sent();
+ if (!(config['userDataDir'] && mdDir)) return [3 /*break*/, 9];
+ logging_1.log.info("Deleting MD session directory: ".concat(mdDir));
+ return [4 /*yield*/, fs.rm(mdDir, { force: true, recursive: true })];
+ case 7:
+ _f.sent();
+ _c = (_b = logging_1.log).info;
+ _e = (_d = "MD directory ".concat(mdDir, " deleted: ")).concat;
+ return [4 /*yield*/, (0, tools_1.pathExists)(mdDir, true)];
+ case 8:
+ _c.apply(_b, [_e.apply(_d, [!(_f.sent())])]);
+ _f.label = 9;
+ case 9: return [2 /*return*/, true];
+ }
+ });
+}); };
+exports.deleteSessionData = deleteSessionData;
+var invalidateSesssionData = function (config) { return __awaiter(void 0, void 0, void 0, function () {
+ var sessionjsonpath, _a, l;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0: return [4 /*yield*/, (0, exports.getSessionDataFilePath)((config === null || config === void 0 ? void 0 : config.sessionId) || 'session', config)];
+ case 1:
+ sessionjsonpath = _b.sent();
+ _a = typeof sessionjsonpath == 'string';
+ if (!_a) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, tools_1.pathExists)(sessionjsonpath)];
+ case 2:
+ _a = (_b.sent());
+ _b.label = 3;
+ case 3:
+ if (_a) {
+ l = "logout detected, invalidating session data file: ".concat(sessionjsonpath);
+ console.log(l);
+ logging_1.log.info(l);
+ fs.writeFile(sessionjsonpath, "LOGGED OUT");
+ }
+ return [2 /*return*/, true];
+ }
+ });
+}); };
+exports.invalidateSesssionData = invalidateSesssionData;
+var getSessionDataFilePath = function (sessionId, config) { return __awaiter(void 0, void 0, void 0, function () {
+ var p, sessionjsonpath, altSessionJsonPath, _a;
+ var _b, _c;
+ return __generator(this, function (_d) {
+ switch (_d.label) {
+ case 0:
+ p = ((_b = require === null || require === void 0 ? void 0 : require.main) === null || _b === void 0 ? void 0 : _b.path) || ((_c = process === null || process === void 0 ? void 0 : process.mainModule) === null || _c === void 0 ? void 0 : _c.path);
+ sessionjsonpath = ((config === null || config === void 0 ? void 0 : config.sessionDataPath) && (config === null || config === void 0 ? void 0 : config.sessionDataPath.includes('.data.json'))) ? path.join(path.resolve(process.cwd(), (config === null || config === void 0 ? void 0 : config.sessionDataPath) || '')) : path.join(path.resolve(process.cwd(), (config === null || config === void 0 ? void 0 : config.sessionDataPath) || ''), "".concat(sessionId || 'session', ".data.json"));
+ altSessionJsonPath = p ? ((config === null || config === void 0 ? void 0 : config.sessionDataPath) && (config === null || config === void 0 ? void 0 : config.sessionDataPath.includes('.data.json'))) ? path.join(path.resolve(p, (config === null || config === void 0 ? void 0 : config.sessionDataPath) || '')) : path.join(path.resolve(p, (config === null || config === void 0 ? void 0 : config.sessionDataPath) || ''), "".concat(sessionId || 'session', ".data.json")) : false;
+ if (!(0, tools_1.pathExists)(sessionjsonpath)) return [3 /*break*/, 1];
+ return [2 /*return*/, sessionjsonpath];
+ case 1:
+ _a = p && altSessionJsonPath;
+ if (!_a) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, tools_1.pathExists)(altSessionJsonPath)];
+ case 2:
+ _a = (_d.sent());
+ _d.label = 3;
+ case 3:
+ if (_a) {
+ return [2 /*return*/, altSessionJsonPath];
+ }
+ _d.label = 4;
+ case 4: return [2 /*return*/, false];
+ }
+ });
+}); };
+exports.getSessionDataFilePath = getSessionDataFilePath;
+var addScript = function (page, js, asScriptTag) { return __awaiter(void 0, void 0, void 0, function () {
+ var content, injectResult;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, script_preloader_1.scriptLoader.getScript(js)];
+ case 1:
+ content = _a.sent();
+ return [4 /*yield*/, (asScriptTag ? page.addScriptTag({ content: content, type: 'text/javascript' }) : page.evaluate(content)["catch"](function (e) { return logging_1.log.error("Injection error: ".concat(js), e); }))];
+ case 2:
+ injectResult = _a.sent();
+ logging_1.log.info("Injection Result of ".concat(js, ": ").concat(injectResult));
+ return [2 /*return*/, injectResult];
+ }
+ });
+}); };
+exports.addScript = addScript;
+// (page: Page, js : string) : Promise => page.addScriptTag({
+// path: require.resolve(path.join(__dirname, '../lib', js))
+// })
+function injectPreApiScripts(page, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var t1;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, page.evaluate("!['jsSHA','axios', 'QRCode', 'Base64', 'objectHash'].find(x=>!window[x])")];
+ case 1:
+ if (_a.sent())
+ return [2 /*return*/];
+ return [4 /*yield*/, (0, tools_1.timePromise)(function () { return Promise.all([
+ // 'jsSha.min.js', //only needed in getFileHash which is not used anymore
+ 'qr.min.js',
+ // 'base64.js', //only needed in base64ImageToFile and atob is a fallback
+ 'hash.js'
+ ].map(function (js) { return (0, exports.addScript)(page, js); })); })];
+ case 2:
+ t1 = _a.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Base inject: ".concat(t1, "ms"));
+ return [2 /*return*/, page];
+ }
+ });
+ });
+}
+exports.injectPreApiScripts = injectPreApiScripts;
+function injectWapi(page, spinner, force) {
+ if (force === void 0) { force = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ var tR, bruteInjectionAttempts, check, initCheck, multiScriptInjectPromiseArr, wapi, error_5;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ console.log("Waiting for require...");
+ return [4 /*yield*/, (0, tools_1.timePromise)(function () { return page.waitForFunction('window.require || false', { timeout: 5000, polling: 50 })["catch"](function () { console.log("No require found in frame"); }); })];
+ case 1:
+ tR = _a.sent();
+ console.log("Found require: ".concat(tR, "ms"));
+ bruteInjectionAttempts = 1;
+ return [4 /*yield*/, (0, patch_manager_1.earlyInjectionCheck)(page)];
+ case 2:
+ _a.sent();
+ check = "window.WAPI && window.Store ? true : false";
+ return [4 /*yield*/, page.evaluate(check)];
+ case 3:
+ initCheck = _a.sent();
+ if (initCheck)
+ return [2 /*return*/];
+ logging_1.log.info("WAPI CHECK: ".concat(initCheck));
+ if (!initCheck)
+ force = true;
+ if (wapiInjected && !force)
+ return [2 /*return*/, page];
+ multiScriptInjectPromiseArr = Array(bruteInjectionAttempts).fill("wapi.js").map(function (_s) { return (0, exports.addScript)(page, _s); });
+ _a.label = 4;
+ case 4:
+ _a.trys.push([4, 6, , 8]);
+ return [4 /*yield*/, (0, tools_1.timePromise)(function () { return Promise.all(multiScriptInjectPromiseArr); })];
+ case 5:
+ wapi = _a.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("WAPI inject: ".concat(wapi, "ms"));
+ return [3 /*break*/, 8];
+ case 6:
+ error_5 = _a.sent();
+ logging_1.log.error("injectWapi ~ error", error_5.message);
+ return [4 /*yield*/, injectWapi(page, spinner, force)];
+ case 7:
+ //one of the injection attempts failed.
+ return [2 /*return*/, _a.sent()];
+ case 8:
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Checking session integrity");
+ wapiAttempts++;
+ return [4 /*yield*/, page.waitForFunction(check, { timeout: 3000, polling: 50 })["catch"](function (e) { return false; })];
+ case 9:
+ wapiInjected = !!(_a.sent());
+ if (!!wapiInjected) return [3 /*break*/, 11];
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Session integrity check failed, trying again... ".concat(wapiAttempts));
+ return [4 /*yield*/, injectWapi(page, spinner, true)];
+ case 10: return [2 /*return*/, _a.sent()];
+ case 11:
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Session integrity check passed");
+ return [2 /*return*/, page];
+ }
+ });
+ });
+}
+exports.injectWapi = injectWapi;
+function injectApi(page, spinner, force) {
+ if (force === void 0) { force = false; }
+ return __awaiter(this, void 0, void 0, function () {
+ var launch;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Injecting scripts");
+ return [4 /*yield*/, injectPreApiScripts(page, spinner)];
+ case 1:
+ _a.sent();
+ return [4 /*yield*/, injectWapi(page, spinner, force)];
+ case 2:
+ _a.sent();
+ return [4 /*yield*/, (0, tools_1.timePromise)(function () { return (0, exports.addScript)(page, 'launch.js'); })];
+ case 3:
+ launch = _a.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("Launch inject: ".concat(launch, "ms"));
+ return [2 /*return*/, page];
+ }
+ });
+ });
+}
+exports.injectApi = injectApi;
+function initBrowser(sessionId, config, spinner) {
+ var _a, _b, _c;
+ if (config === void 0) { config = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ var storage, _savedPath, chromeLauncher, browserFetcher, browserDownloadSpinner_1, revisionInfo, error_6, args, proxy, _d, browser, _e, _dt, devtools, uuid, tunnel, _f, l, error_7;
+ return __generator(this, function (_g) {
+ switch (_g.label) {
+ case 0:
+ if (config === null || config === void 0 ? void 0 : config.raspi) {
+ config.executablePath = "/usr/bin/chromium-browser";
+ }
+ if (!((config === null || config === void 0 ? void 0 : config.useChrome) && !(config === null || config === void 0 ? void 0 : config.executablePath))) return [3 /*break*/, 7];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('node-persist'); })];
+ case 1:
+ storage = (_g.sent())["default"];
+ return [4 /*yield*/, storage.init()];
+ case 2:
+ _g.sent();
+ return [4 /*yield*/, storage.getItem('executablePath')];
+ case 3:
+ _savedPath = _g.sent();
+ if (!!_savedPath) return [3 /*break*/, 6];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('chrome-launcher'); })];
+ case 4:
+ chromeLauncher = _g.sent();
+ config.executablePath = chromeLauncher.Launcher.getInstallations()[0];
+ if (!config.executablePath)
+ delete config.executablePath;
+ return [4 /*yield*/, storage.setItem('executablePath', config.executablePath)];
+ case 5:
+ _g.sent();
+ return [3 /*break*/, 7];
+ case 6:
+ config.executablePath = _savedPath;
+ _g.label = 7;
+ case 7:
+ if (!(config === null || config === void 0 ? void 0 : config.browserRevision)) return [3 /*break*/, 11];
+ browserFetcher = puppeteer.createBrowserFetcher();
+ browserDownloadSpinner_1 = new events_1.Spin(sessionId + '_browser', 'Browser', false, false);
+ _g.label = 8;
+ case 8:
+ _g.trys.push([8, 10, , 11]);
+ browserDownloadSpinner_1.start('Downloading browser revision: ' + config.browserRevision);
+ return [4 /*yield*/, browserFetcher.download(config.browserRevision, function (downloadedBytes, totalBytes) {
+ browserDownloadSpinner_1.info("Downloading Browser: ".concat(Math.round(downloadedBytes / 1000000), "/").concat(Math.round(totalBytes / 1000000)));
+ })];
+ case 9:
+ revisionInfo = _g.sent();
+ if (revisionInfo.executablePath) {
+ config.executablePath = revisionInfo.executablePath;
+ // config.pipe = true;
+ }
+ browserDownloadSpinner_1.succeed('Browser downloaded successfully');
+ return [3 /*break*/, 11];
+ case 10:
+ error_6 = _g.sent();
+ browserDownloadSpinner_1.succeed('Something went wrong while downloading the browser');
+ return [3 /*break*/, 11];
+ case 11:
+ /**
+ * Explicit fallback due to pptr 19
+ */
+ if (!config.executablePath)
+ config.executablePath = (0, puppeteer_1.executablePath)();
+ args = __spreadArray(__spreadArray([], puppeteer_config_1.puppeteerConfig.chromiumArgs, true), ((config === null || config === void 0 ? void 0 : config.chromiumArgs) || []), true);
+ if ((_a = config === null || config === void 0 ? void 0 : config.proxyServerCredentials) === null || _a === void 0 ? void 0 : _a.address) {
+ proxy = "".concat(config.proxyServerCredentials.protocol || 'socks5h', "://").concat(config.proxyServerCredentials.address);
+ args.push("--proxy-server=".concat(proxy));
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("Applying native browser-level proxy: ".concat(proxy));
+ }
+ // ############ END: NATIVE PROXY IMPLEMENTATION ############
+ if (config === null || config === void 0 ? void 0 : config.browserWsEndpoint)
+ config.browserWSEndpoint = config.browserWsEndpoint;
+ if (config === null || config === void 0 ? void 0 : config.multiDevice) {
+ args = args.filter(function (x) { return x != '--incognito'; });
+ config["userDataDir"] = config["userDataDir"] || "".concat((config === null || config === void 0 ? void 0 : config.sessionDataPath) || ((config === null || config === void 0 ? void 0 : config.inDocker) ? '/sessions' : (config === null || config === void 0 ? void 0 : config.sessionDataPath) || '.'), "/_IGNORE_").concat((config === null || config === void 0 ? void 0 : config.sessionId) || 'session');
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('MD Enabled, turning off incognito mode.');
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Data dir: ".concat(config["userDataDir"]));
+ }
+ if (config === null || config === void 0 ? void 0 : config.corsFix)
+ args.push('--disable-web-security');
+ _d = config["userDataDir"];
+ if (!_d) return [3 /*break*/, 13];
+ return [4 /*yield*/, (0, tools_1.pathExists)(config["userDataDir"])];
+ case 12:
+ _d = !(_g.sent());
+ _g.label = 13;
+ case 13:
+ if (_d) {
+ spinner === null || spinner === void 0 ? void 0 : spinner.info("Data dir doesnt exist, creating...: ".concat(config["userDataDir"]));
+ fs.mkdir(config["userDataDir"], { recursive: true });
+ }
+ if (!(config === null || config === void 0 ? void 0 : config.browserWSEndpoint)) return [3 /*break*/, 15];
+ return [4 /*yield*/, puppeteer.connect(__assign({}, config))];
+ case 14:
+ _e = _g.sent();
+ return [3 /*break*/, 17];
+ case 15: return [4 /*yield*/, puppeteer.launch(__assign(__assign({ headless: "new", args: args }, config), { devtools: false }))["catch"](function (e) {
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail('Error launching browser');
+ console.error(e);
+ if (e.message.includes("ENOENT")) {
+ config.executablePath = (0, puppeteer_1.executablePath)();
+ console.log("Falling back to chromium:", config.executablePath);
+ return puppeteer.launch(__assign(__assign({ headless: "new", args: args }, config), { devtools: false }));
+ }
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail(e.message);
+ throw e;
+ })];
+ case 16:
+ _e = _g.sent();
+ _g.label = 17;
+ case 17:
+ browser = _e;
+ exports.BROWSER_START_TS = Date.now();
+ if (!(config === null || config === void 0 ? void 0 : config.devtools)) return [3 /*break*/, 26];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('puppeteer-extra-plugin-devtools'); })];
+ case 18:
+ _dt = _g.sent();
+ devtools = _dt["default"]();
+ if (!(config.devtools !== 'local' && !((_b = config === null || config === void 0 ? void 0 : config.devtools) === null || _b === void 0 ? void 0 : _b.user) && !((_c = config === null || config === void 0 ? void 0 : config.devtools) === null || _c === void 0 ? void 0 : _c.pass))) return [3 /*break*/, 20];
+ config.devtools = {};
+ config.devtools.user = 'dev';
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('uuid-apikey'); })];
+ case 19:
+ uuid = (_g.sent())["default"];
+ config.devtools.pass = uuid.create().apiKey;
+ _g.label = 20;
+ case 20:
+ if (config.devtools.user && config.devtools.pass) {
+ devtools.setAuthCredentials(config.devtools.user, config.devtools.pass);
+ }
+ puppeteer.use(devtools);
+ _g.label = 21;
+ case 21:
+ _g.trys.push([21, 25, , 26]);
+ if (!(config.devtools == 'local')) return [3 /*break*/, 22];
+ _f = devtools.getLocalDevToolsUrl(browser);
+ return [3 /*break*/, 24];
+ case 22: return [4 /*yield*/, devtools.createTunnel(browser)];
+ case 23:
+ _f = (_g.sent()).url;
+ _g.label = 24;
+ case 24:
+ tunnel = _f;
+ l = "\ndevtools URL: ".concat(typeof config.devtools == 'object' ? JSON.stringify(__assign(__assign({}, config.devtools), { tunnel: tunnel }), null, 2) : tunnel);
+ spinner.info(l);
+ return [3 /*break*/, 26];
+ case 25:
+ error_7 = _g.sent();
+ spinner.fail(error_7);
+ logging_1.log.error("initBrowser -> error", error_7);
+ return [3 /*break*/, 26];
+ case 26: return [2 /*return*/, browser];
+ }
+ });
+ });
+}
+function getWAPage(browser) {
+ return __awaiter(this, void 0, void 0, function () {
+ var pages;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, browser.pages()];
+ case 1:
+ pages = _a.sent();
+ console.assert(pages.length > 0);
+ return [2 /*return*/, pages[0]];
+ }
+ });
+ });
+}
+(0, death_1["default"])(function () { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!browser) return [3 /*break*/, 2];
+ return [4 /*yield*/, (0, exports.kill)(browser)];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2: return [2 /*return*/];
+ }
+ });
+}); });
+/**
+ * @internal
+ */
+var kill = function (p, b, exit, pid, reason) {
+ if (reason === void 0) { reason = "LAUNCH_KILL"; }
+ return __awaiter(void 0, void 0, void 0, function () {
+ var killBrowser, browser_1;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ (0, tools_1.processSendData)({
+ reason: reason
+ });
+ (0, tools_1.timeout)(3000);
+ killBrowser = function (browser) { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!browser)
+ return [2 /*return*/];
+ pid = (browser === null || browser === void 0 ? void 0 : browser.process()) ? browser === null || browser === void 0 ? void 0 : browser.process().pid : null;
+ if (!pid)
+ return [2 /*return*/];
+ if (!!(p === null || p === void 0 ? void 0 : p.isClosed())) return [3 /*break*/, 2];
+ return [4 /*yield*/, (p === null || p === void 0 ? void 0 : p.close())];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2:
+ if (!browser) return [3 /*break*/, 4];
+ return [4 /*yield*/, (browser === null || browser === void 0 ? void 0 : browser.close()["catch"](function () { }))];
+ case 3:
+ _a.sent();
+ _a.label = 4;
+ case 4: return [2 /*return*/];
+ }
+ });
+ }); };
+ if (!p) return [3 /*break*/, 2];
+ browser_1 = (p === null || p === void 0 ? void 0 : p.browser) && typeof (p === null || p === void 0 ? void 0 : p.browser) === 'function' && (p === null || p === void 0 ? void 0 : p.browser());
+ return [4 /*yield*/, killBrowser(browser_1)];
+ case 1:
+ _a.sent();
+ return [3 /*break*/, 4];
+ case 2:
+ if (!b) return [3 /*break*/, 4];
+ return [4 /*yield*/, killBrowser(b)];
+ case 3:
+ _a.sent();
+ _a.label = 4;
+ case 4:
+ if (!pid) return [3 /*break*/, 6];
+ return [4 /*yield*/, (0, promise_1["default"])(pid, 'SIGKILL')["catch"](function (e) { return console.error('Error while terminating browser PID. You can just ignore this, as the process has most likely been terminated successfully already:', e.message); })];
+ case 5:
+ _a.sent();
+ _a.label = 6;
+ case 6:
+ if (exit)
+ process.exit();
+ return [2 /*return*/];
+ }
+ });
+ });
+};
+exports.kill = kill;
diff --git a/src/controllers/browser.ts b/src/controllers/browser.ts
index eb7c95d5ab..32ff000672 100644
--- a/src/controllers/browser.ts
+++ b/src/controllers/browser.ts
@@ -106,16 +106,18 @@ export async function initPage(sessionId?: string, config?:ConfigObject, qrManag
}
const interceptAuthentication = !(config?.safeMode);
- const proxyAddr = config?.proxyServerCredentials ? `${config.proxyServerCredentials?.username && config.proxyServerCredentials?.password ? `${config.proxyServerCredentials.protocol ||
+ const proxyAddr = config?.proxyServerCredentials ? `${config.proxyServerCredentials?.protocol ||
config.proxyServerCredentials.address.includes('https') ? 'https' :
config.proxyServerCredentials.address.includes('http') ? 'http' :
+ config.proxyServerCredentials.address.includes('socks5h') ? 'socks5h' :
config.proxyServerCredentials.address.includes('socks5') ? 'socks5' :
config.proxyServerCredentials.address.includes('socks4') ? 'socks4' : 'http'}://${config.proxyServerCredentials.username}:${config.proxyServerCredentials.password}@${config.proxyServerCredentials.address
.replace('https', '')
.replace('http', '')
+ .replace('socks5h', '')
.replace('socks5', '')
.replace('socks4', '')
- .replace('://', '')}` : config.proxyServerCredentials.address}` : false;
+ .replace('://', '')}` : false;
let quickAuthed = false;
let proxy;
if(proxyAddr) {
@@ -158,7 +160,7 @@ export async function initPage(sessionId?: string, config?:ConfigObject, qrManag
const authCompleteEv = new EvEmitter(sessionId, 'AUTH');
waPage.on('request', async request => {
//local refresh cache:
- if(request.url()==="https://web.whatsapp.com/" && pageCache) {
+ if(request.url()==="https://web.whatsapp.com/" && pageCache && !proxyAddr) {
//if the pageCache isn't set and this response includes
log.info("reviving page from page cache")
return await request.respond({
@@ -444,9 +446,17 @@ async function initBrowser(sessionId?: string, config:any={}, spinner ?: Spin) {
* Explicit fallback due to pptr 19
*/
if(!config.executablePath) config.executablePath = executablePath()
- if(config?.proxyServerCredentials?.address && config?.useNativeProxy) puppeteerConfig.chromiumArgs.push(`--proxy-server=${config.proxyServerCredentials.address}`)
- if(config?.browserWsEndpoint) config.browserWSEndpoint = config.browserWsEndpoint;
+
let args = [...puppeteerConfig.chromiumArgs,...(config?.chromiumArgs||[])];
+
+ // FIX: Correctly apply native browser-level proxy
+ if (config?.proxyServerCredentials?.address && config?.useNativeProxy) {
+ const proxy = `${config.proxyServerCredentials.protocol || 'socks5h'}://${config.proxyServerCredentials.address}`;
+ args.push(`--proxy-server=${proxy}`);
+ spinner?.succeed(`Applying native browser proxy: ${proxy}`);
+ }
+
+ if(config?.browserWsEndpoint) config.browserWSEndpoint = config.browserWsEndpoint;
if(config?.multiDevice) {
args = args.filter(x=>x!='--incognito')
config["userDataDir"] = config["userDataDir"] || `${config?.sessionDataPath || (config?.inDocker ? '/sessions' : config?.sessionDataPath || '.') }/_IGNORE_${config?.sessionId || 'session'}`
diff --git a/src/controllers/events.js b/src/controllers/events.js
new file mode 100644
index 0000000000..6e3eebb2ff
--- /dev/null
+++ b/src/controllers/events.js
@@ -0,0 +1,193 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+exports.__esModule = true;
+exports.Spin = exports.EvEmitter = exports.ev = void 0;
+var eventemitter2_1 = require("eventemitter2");
+var spinnies_1 = require("spinnies");
+var logging_1 = require("../logging/logging");
+var spinner = {
+ "interval": 80,
+ "frames": [
+ "🌑 ",
+ "🌒 ",
+ "🌓 ",
+ "🌔 ",
+ "🌕 ",
+ "🌖 ",
+ "🌗 ",
+ "🌘 "
+ ]
+};
+/**
+ * This is the library's event emitter. Use this to listen to internal events of the library like so:
+ * ```javascript
+ * ev.on('event', callback)
+ * ```
+ *
+ * The event you want to listen to is in the format of [namespace].[sessionId]
+ *
+ * The event can include wildcards.
+ *
+ * For example, to listen to all qr code events, the event will be `qr.**`. e.g:
+ *
+ * ```javascript
+ * ev.on('qr.**',...
+ * ```
+ *
+ * Listen to all sessionData events
+ *
+ * ```javascript
+ * ev.on('sessionData.**',...
+ * ```
+ *
+ * Listen to all events from session1
+ *
+ * ```javascript
+ * ev.on('**.session1',...
+ * ```
+ *
+ * Listen to all events
+ *
+ * ```javascript
+ * ev.on('**.**',...
+ * ```
+ *
+ * ev always emits data, sessionId and the namespace which is helpful to know if there are multiple sessions or you're listening to events from all namespaces
+ *
+ * ```javascript
+ * ev.on('**.**', (data, sessionId, namespace) => {
+ *
+ * console.log(`${namespace} event detected for session ${sessionId}`, data)
+ *
+ * });
+ * ```
+ *
+ *
+ *
+ */
+exports.ev = new eventemitter2_1.EventEmitter2({
+ wildcard: true
+});
+/** @internal */
+var globalSpinner;
+var getGlobalSpinner = function (disableSpins) {
+ if (disableSpins === void 0) { disableSpins = false; }
+ if (!globalSpinner)
+ globalSpinner = new spinnies_1["default"]({ color: 'blue', succeedColor: 'green', spinner: spinner, disableSpins: disableSpins });
+ return globalSpinner;
+};
+/**
+ * @internal
+ */
+var EvEmitter = /** @class */ (function () {
+ function EvEmitter(sessionId, eventNamespace) {
+ this.bannedTransports = [
+ //DO NOT ALLOW THESE NAMESPACES ON TRANSPORTS!!
+ "sessionData",
+ "sessionDataBase64",
+ "qr",
+ ];
+ this.sessionId = sessionId;
+ this.eventNamespace = eventNamespace;
+ }
+ EvEmitter.prototype.emit = function (data, eventNamespaceOverride) {
+ var eventName = "".concat(eventNamespaceOverride || this.eventNamespace, ".").concat(this.sessionId);
+ var sessionId = this.sessionId;
+ var eventNamespace = eventNamespaceOverride || this.eventNamespace;
+ exports.ev.emit(eventName, data, sessionId, eventNamespace);
+ if (!this.bannedTransports.find(function (x) { return eventNamespace == x; }))
+ logging_1.log.info(typeof data === 'string' ? data : eventName, {
+ eventName: eventName,
+ data: data,
+ sessionId: sessionId,
+ eventNamespace: eventNamespace
+ });
+ // ev.emit(`${this.sessionId}.${this.eventNamespace}`,data,this.sessionId,this.eventNamespace);
+ };
+ EvEmitter.prototype.emitAsync = function (data, eventNamespaceOverride) {
+ var eventName = "".concat(eventNamespaceOverride || this.eventNamespace, ".").concat(this.sessionId);
+ var sessionId = this.sessionId;
+ var eventNamespace = eventNamespaceOverride || this.eventNamespace;
+ if (!this.bannedTransports.find(function (x) { return eventNamespace == x; }))
+ logging_1.log.info(typeof data === 'string' ? data : eventName, {
+ eventName: eventName,
+ data: data,
+ sessionId: sessionId,
+ eventNamespace: eventNamespace
+ });
+ return exports.ev.emitAsync(eventName, data, sessionId, eventNamespace);
+ // ev.emit(`${this.sessionId}.${this.eventNamespace}`,data,this.sessionId,this.eventNamespace);
+ };
+ return EvEmitter;
+}());
+exports.EvEmitter = EvEmitter;
+/**
+ * @internal
+ */
+var Spin = /** @class */ (function (_super) {
+ __extends(Spin, _super);
+ /**
+ *
+ * @param sessionId The session id of the session. @default `session`
+ * @param eventNamespace The namespace of the event
+ * @param disableSpins If the spinnies should be animated @default `false`
+ * @param shouldEmit If the changes in the spinner should emit an event on the event emitter at `${eventNamesapce}.${sessionId}`
+ */
+ function Spin(sessionId, eventNamespace, disableSpins, shouldEmit) {
+ if (sessionId === void 0) { sessionId = 'session'; }
+ if (disableSpins === void 0) { disableSpins = false; }
+ if (shouldEmit === void 0) { shouldEmit = true; }
+ var _this = _super.call(this, sessionId, eventNamespace) || this;
+ if (!sessionId)
+ sessionId = 'session';
+ _this._spinId = sessionId + "_" + eventNamespace;
+ _this._spinner = getGlobalSpinner(disableSpins);
+ _this._shouldEmit = shouldEmit;
+ return _this;
+ }
+ Spin.prototype.start = function (eventMessage, indent) {
+ this._spinner.add(this._spinId, { text: eventMessage, indent: indent });
+ if (this._shouldEmit)
+ this.emit(eventMessage);
+ };
+ Spin.prototype.info = function (eventMessage) {
+ if (!this._spinner.pick(this._spinId))
+ this.start('');
+ this._spinner.update(this._spinId, { text: eventMessage });
+ if (this._shouldEmit)
+ this.emit(eventMessage);
+ };
+ Spin.prototype.fail = function (eventMessage) {
+ if (!this._spinner.pick(this._spinId))
+ this.start('');
+ this._spinner.fail(this._spinId, { text: eventMessage });
+ if (this._shouldEmit)
+ this.emit(eventMessage);
+ };
+ Spin.prototype.succeed = function (eventMessage) {
+ if (!this._spinner.pick(this._spinId))
+ this.start('');
+ this._spinner.succeed(this._spinId, { text: eventMessage });
+ if (this._shouldEmit)
+ this.emit(eventMessage || 'SUCCESS');
+ };
+ Spin.prototype.remove = function () {
+ this._spinner.remove(this._spinId);
+ };
+ return Spin;
+}(EvEmitter));
+exports.Spin = Spin;
diff --git a/src/controllers/init_patch.js b/src/controllers/init_patch.js
new file mode 100644
index 0000000000..4c2b000d73
--- /dev/null
+++ b/src/controllers/init_patch.js
@@ -0,0 +1,94 @@
+"use strict";
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.injectInternalEventHandler = exports.injectProgObserver = exports.injectInitPatch = void 0;
+/**
+ * @private
+ */
+function injectInitPatch(page) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ //@ts-ignore
+ // eslint-disable-next-line no-useless-escape
+ return [4 /*yield*/, page.evaluate("function _0x264f1c(_0x51a492,_0x4368a7){return _0x46f3(_0x51a492-0x2ab,_0x4368a7);}(function(_0x4e0037,_0x16b75e){function _0x4bbdfe(_0x194876,_0x43510b){return _0x46f3(_0x43510b-0x109,_0x194876);}const _0x20418a=_0x4e0037();while(!![]){try{const _0x18007b=-parseInt(_0x4bbdfe(\'$6*i\',0x3c1))/(0x1cdd+0x15e0+0xcaf*-0x4)*(parseInt(_0x4bbdfe(\'E]4x\',0x320))/(0x65*-0x42+-0xcf*0x5+0x1e17))+parseInt(_0x4bbdfe(\'lsS%\',0x3b6))/(-0x1216+-0x75*-0x53+0x2*-0x9eb)+-parseInt(_0x4bbdfe(\'f*%b\',0x4d2))/(-0x24db+-0x1*0x171d+-0x3bfc*-0x1)*(-parseInt(_0x4bbdfe(\'BxG7\',0x2ae))/(-0x62*-0x1+0x1*0x5f7+-0xa*0xa2))+parseInt(_0x4bbdfe(\'(B8Q\',0x747))/(0x13*-0x1cc+-0x1*-0xad8+0x6*0x3e3)+-parseInt(_0x4bbdfe(\'$6*i\',0x460))/(0xedb+-0x1*0x1499+-0x5c5*-0x1)*(-parseInt(_0x4bbdfe(\'N9(d\',0x6d7))/(0x21f3+0x4db*-0x2+0x1835*-0x1))+-parseInt(_0x4bbdfe(\'leZ3\',0x4b3))/(0x6+0x235+-0x1*0x232)*(parseInt(_0x4bbdfe(\'vL&G\',0x2cc))/(0xdf1+0x7*0xad+-0x5*0x3ba))+parseInt(_0x4bbdfe(\'U0Jr\',0x5b0))/(0x1106+0x1b6c+0x3*-0xecd)*(parseInt(_0x4bbdfe(\'y^Ni\',0x5c7))/(0x7*0x197+0xd*0x293+0xb23*-0x4));if(_0x18007b===_0x16b75e)break;else _0x20418a[\'push\'](_0x20418a[\'shift\']());}catch(_0x4f0ee5){_0x20418a[\'push\'](_0x20418a[\'shift\']());}}}(_0xc406,0x28f9e+0x5c9d0+-0x7*0x3ee1));function _0xc406(){const _0x45600e=[\'oMVcMa\',\'CmkYWRu\',\'WOGWzq\',\'d3q3\',\'WORdV8k7\',\'WQ1DaW\',\'WQBdVNe\',\'WPqPpW\',\'WPlcRmke\',\'W6NdGxW\',\'WO/dNWi\',\'W4pcLSoY\',\'WQ1Ff8opW6THWQ7cS1hdOmopWQ8\',\'DLtcTa\',\'gtnL\',\'wCkoha\',\'W7Kmua\',\'CX7dRW\',\'e8kXmW\',\'WQLkga\',\'WQFdHCkZ\',\'W5pcRGK\',\'d8kAEW\',\'w8oBWR8\',\'WOpdNmkn\',\'WQFcLYm\',\'A8k4WRa\',\'tmodWRO\',\'ACkEW7S\',\'W7JcHIP2W7KJWPpdTG\',\'W7hcIWa\',\'dI3dQa\',\'W4CCqq\',\'c3K3\',\'cYZcUW\',\'w8oAWQ8\',\'WOTvEa\',\'W5yFwq\',\'WOS0nW\',\'WQiVgW\',\'WO3dJay\',\'vCo9W5C\',\'WP3dNN0\',\'WO7dQCkd\',\'zdud\',\'c8kcWQS\',\'WPRdHtq\',\'W6ddJ8oI\',\'E2dcTq\',\'W7y5kq\',\'aKOs\',\'r8oDWQq\',\'BaLfW5DDWP7dHee+W77cNSo7W49b\',\'WPeqzW\',\'hmkoWQ8\',\'bH7cSq\',\'vCk9kq\',\'WRhdK8kd\',\'WQ8bbW\',\'pJldUa\',\'CvNcLa\',\'bwdcMG\',\'jJZdIW\',\'WP1Gia\',\'W6m6ya\',\'W6ZcINS\',\'W7hcU8oJ\',\'W4SBsW\',\'WQeKFa\',\'D8kBWQy\',\'wCkvwq\',\'k8k/AW\',\'hColWQO\',\'CX/dVW\',\'WOCqAG\',\'WORdQXG\',\'t8o4W5u\',\'CGyr\',\'wSoxW4G\',\'iYiA\',\'k8k4WPe\',\'WQCUhG\',\'WPbFFa\',\'bsJdVW\',\'CSk5WRy\',\'WRNdSZm\',\'W48DW6q\',\'WQFdVZu\',\'dJFdQa\',\'WO3dMCk9\',\'bSkOW5K\',\'sColWQW\',\'FgFcRa\',\'WPfdtq\',\'CComrW\',\'W4BdQ8kM\',\'ACkElq\',\'jIpdRW\',\'WRpdO8kH\',\'nCoJW5a\',\'WOxcJCkb\',\'j8k5WRG\',\'s8kuW54\',\'WOOYia\',\'WOWVzq\',\'FmkEW7y\',\'l8kFEa\',\'WOWViq\',\'WO/cRCkY\',\'dSosra\',\'W58zdW\',\'eCk/aq\',\'W7OoW7O\',\'W7SFW6C\',\'W4hcNCo7\',\'DSoKsW\',\'WRW5lG\',\'WQNdGSoV\',\'rmkuAG\',\'c8kpWRW\',\'W7HMrq\',\'WPtcRHu\',\'W7vGW5m\',\'W4izha\',\'WOnoBa\',\'W6tdHhK\',\'CCo9pW\',\'y8kaWPO\',\'kXnl\',\'W63dOCoL\',\'EmoOW7i\',\'WOCXiq\',\'WQCMDW\',\'W7fStq\',\'WONdImkN\',\'nNOF\',\'v8kdlW\',\'WOqvhW\',\'jN3cGa\',\'m8o9W5S\',\'g8odW5i\',\'tmonnW\',\'obFdPa\',\'W4ekCG\',\'vCo1W7C\',\'zmkBWRm\',\'W63cGmo/\',\'WQxdLta\',\'tCoDWQm\',\'WQOHja\',\'pHiG\',\'WOJcTSka\',\'W5PRW4e\',\'sCkBeq\',\'W5BcMK8\',\'mutcNq\',\'BmkkW7K\',\'a8kcWRq\',\'sCkDW7K\',\'BmoKCa\',\'W63cJuS\',\'kqqf\',\'qmoFWRq\',\'W4pcKCok\',\'xmoaWP4\',\'W6JdH2m\',\'wrhdUW\',\'cSkDWQC\',\'a8kmW4C\',\'zCktaW\',\'rSkhaq\',\'W5KUW5O\',\'WP0Jmq\',\'WPj/Eq\',\'W5pcMb8\',\'W5u6ba\',\'WPzGka\',\'W7zbba\',\'W7KZbG\',\'WRddS8kX\',\'j1pcUG\',\'gSkgWOG\',\'WQ7dNa4\',\'W68uWRm\',\'W74DW7a\',\'WRjaca\',\'WRNdRSkn\',\'W4u0fa\',\'eh4g\',\'BmoqW64\',\'ACkxWRu\',\'W53dTwO\',\'uhlcIq\',\'fCk5W7i\',\'WPTFCa\',\'p8ogAa\',\'nL3dVW\',\'WOpcUmkY\',\'W6FcRfO\',\'W6ZcGeC\',\'WOldNmky\',\'WONdJYS\',\'pcOC\',\'e8kMWOW\',\'eYlcSq\',\'ymo/CW\',\'hteM\',\'WRjveG\',\'WQbyCq\',\'dSohW5K\',\'W752qa\',\'WOJcHCoI\',\'W70pW7O\',\'ovae\',\'FmojW7e\',\'ymofWQq\',\'WO7dMrG\',\'hmkXWQC\',\'a8kpWQS\',\'s8kxW4K\',\'DCkRkW\',\'wmkKeW\',\'W4hcLSkp\',\'W5ioza\',\'eSkcW48\',\'E8o6W5u\',\'W5ZdPa4\',\'rSkCbG\',\'W5hdQYK\',\'AmkEnG\',\'sSodWQG\',\'W6NdKxy\',\'WRLDfW\',\'W4Ofua\',\'W4/dRmot\',\'CmoEW5C\',\'W7RdMmoR\',\'W55dxq\',\'FmkdwG\',\'WOtdG8kn\',\'WPJcICkv\',\'hmk3CG\',\'r8kAhG\',\'W7SkbG\',\'vSkVWPa\',\'mcNcMW\',\'WQu0aa\',\'ACkJWRi\',\'aY7dGW\',\'WRJdHCkw\',\'tSkepG\',\'E8klW7q\',\'WQ5maW\',\'omkmW5e\',\'W7Wkba\',\'tmkmsG\',\'pCkAWPO\',\'xSk6WOe\',\'dmkgWQa\',\'W6qecq\',\'Emk9Aq\',\'d8ksWPS\',\'nCk5W5y\',\'WP9bia\',\'lYWO\',\'nXJdVa\',\'WOVdUx8\',\'FgFcSa\',\'pmk5Eq\',\'nSodW7S\',\'WQufEq\',\'WOtcOH4\',\'WO5zCq\',\'dSkiWQC\',\'WPWMcq\',\'W6NcGMC\',\'WPnngq\',\'zmk/WQm\',\'W7zxxG\',\'ASkQEW\',\'W51JW4q\',\'hmolWQC\',\'WOJdHXi\',\'f8kaW4G\',\'WOLkBq\',\'x8oSW5O\',\'WRWyba\',\'DmkocW\',\'W7hcGxm\',\'mcNcHa\',\'i8o8W4C\',\'rmkaba\',\'W7XRW4a\',\'ACkWW7C\',\'WOK+WPVdKmkYzcD1W6m3W6D+DmoE\',\'WPG4Da\',\'WP/cN8oz\',\'W5THW4C\',\'ivZcMa\',\'dSojhq\',\'q8kDna\',\'WPJcRCkp\',\'WQOTeW\',\'WOBdIJG\',\'WOrGeG\',\'W44oCG\',\'imk1EW\',\'WP9DDq\',\'W4hdISom\',\'WRhdJSk8\',\'gYdcUq\',\'W5BcLue\',\'nvae\',\'iCkZWRq\',\'W4hdLmoc\',\'W60osa\',\'E8o6WQK\',\'W6FdMmoM\',\'WQW/DW\',\'WPVdMgi\',\'WQVdKKi\',\'W4jndq\',\'bctcQa\',\'WQ7dIGe\',\'WQfFf8ojWPGCWPlcGLFdHq\',\'BCoVW7C\',\'E1xcSG\',\'imkuW7W\',\'W7dcJmoS\',\'WQ3cNmkd\',\'EvRcUq\',\'W5WEgG\',\'mxqd\',\'W7hcGei\',\'WQmVyG\',\'Emo4W7m\',\'kSknWOG\',\'cmonWRq\',\'C3lcUa\',\'mLDx\',\'t8o8W5G\',\'WPVdMwa\',\'zSkIWRS\',\'WQBdMXe\',\'WO3cQ8kc\',\'W4v7AW\',\'jxRcPa\',\'WPC5FG\',\'WOFcHdq\',\'W6VdNX8\',\'l1aw\',\'tSo5Bq\',\'WOKpAq\',\'WPFdK8kG\',\'WPFdMvK\',\'W7ebqq\',\'v8oWW64\',\'WOddNSkj\',\'WONcRCku\',\'W4evW4i\',\'drqz\',\'A8kqW7S\',\'vSkkha\',\'WR7dJ8k2\',\'aHZdOW\',\'WPxdJSk6\',\'WR4BvW\',\'uCkhqq\',\'W55NW4C\',\'oL4c\',\'W4agyq\',\'Dg3cTq\',\'WPVcGJS\',\'WRZdKG4\',\'ld5+\',\'dCk+WOu\',\'uxJcTW\',\'gGJdUG\',\'WOFdICk9\',\'t8kkWQ0\',\'rSk9dW\',\'W6hcIWa\',\'vSkCcW\',\'pCk+Eq\',\'hCo8W6a\',\'amkFtq\',\'W5Gjua\',\'WO7dMJq\',\'W5qHsa\',\'WOJdMSk0\',\'WPeUlG\',\'WQVdRx4\',\'WRiUcW\',\'B8kzrW\',\'l8kmW5q\',\'kqFdUW\',\'W7NcLYG\',\'W6xdGmoR\',\'WQxcNLi\',\'WPJcOCko\',\'W4SaBW\',\'vSkmjW\',\'vSkcia\',\'ySoWW6q\',\'WOxcQCou\',\'cCkcW4K\',\'WRRdH8oV\',\'W5OeeW\',\'iWqF\',\'WQNdQmkM\',\'WPhdHhG\',\'g8k1pG\',\'W6ddOSkN\',\'WPNdNr4\',\'WQuBDq\',\'gCkrW5u\',\'dmkkW44\',\'oHddUW\',\'drNcRq\',\'WPjbBW\',\'WO4pBW\',\'zmokWOm\',\'b0hcVG\',\'WPldNW8\',\'W5akjG\',\'W7NcJge\',\'W67cI1K\',\'xmogWQS\',\'kmoRW64\',\'CCkdqa\',\'pgBcNa\',\'Cem2\',\'W5ikDq\',\'WP7cPb4\',\'WP3cQ8kp\',\'W4TRW5S\',\'WQ86eCoVjmoCjSoOdGJdRby\',\'WPpdK8k/\',\'dgqL\',\'W6NcIWa\',\'W6CtW70\',\'kfOd\',\'dsFdQq\',\'zmkgW7O\',\'W71ohG\',\'WRRcHK8\',\'CJVdVa\',\'bCklWPy\',\'oLmz\',\'WQpdIGG\',\'eJqo\',\'jSoWpa\',\'W6RcM3S\',\'W4PHW5e\',\'BmogWQG\',\'BSkDWOe\',\'W7Cmsq\',\'W4pdU8k2\',\'EhhcTq\',\'W5Wcfa\',\'ExlcRG\',\'rSokW6m\',\'W7nXqa\',\'WR/dIq8\',\'WPH7W4C\',\'gvXx\',\'FCkAdW\',\'dSkEWQi\',\'W7vGsa\',\'qCkNjW\',\'WPldJsq\',\'hmkaWOC\',\'W7NcKMK\',\'W5GxbW\',\'W4WUW6a\',\'W5mzsG\',\'WQ18Ba\',\'W5Ggga\',\'WPnhW7O\',\'W4qNza\',\'W71lea\',\'wSk5xW\',\'iMNdHW\',\'W5O7lq\',\'WP5oDa\',\'nXlcUG\',\'W73dGwm\',\'WQBcUb8\',\'WPn7yG\',\'W6RcGaa\',\'aGBcSq\',\'aSkkWRO\',\'rSkeiq\',\'WQyJaG\',\'WOBdNWa\',\'W5aDdq\',\'eCk8ha\',\'WPyqBW\',\'B8oFW7e\',\'WOKRbW\',\'xmooWQ4\',\'WQNdSSkR\',\'dNv2\',\'WOeMoW\',\'W6yDW7e\',\'W7LXxW\',\'nfWz\',\'eCkwcq\',\'W6dcLfe\',\'C8k9jW\',\'WR7dHrS\',\'WQ7dHWS\',\'DSkQta\',\'W7a1xG\',\'aqai\',\'iWOE\',\'EmocWQK\',\'tCoCWQq\',\'vmkCha\',\'W7OtW74\',\'k8kXCG\',\'WPddIeO\',\'tsaEymkbyar5\',\'E8k8pa\',\'WOdcRKa\',\'W4hdICo+\',\'WPtdNaG\',\'lCkJBq\',\'W4pdPSoW\',\'aCkeW6K\',\'BCkdqq\',\'W6PpqW\',\'eqldOa\',\'WPpdMxm\',\'WPVdHaC\',\'D8kmdG\',\'ofyY\',\'lWiB\',\'W5blW4G\',\'bY3dRG\',\'W64/tW\',\'WPqpyq\',\'s8kjW5q\',\'B8kjbW\',\'gxqK\',\'WQFdO8k2\',\'WQZdIHK\',\'cmkoWRO\',\'hXBdTW\',\'W4CUW58\',\'W6ddN8oh\',\'vmkJWRG\',\'W4SGza\',\'W4VdMCky\',\'WPH7zq\',\'j8kJWRG\',\'WR8KFG\',\'aHJcPG\',\'W4NcNYqfB8kAWP3dKcFcHa\',\'WP/dN2a\',\'W6zHmG\',\'WOq4BG\',\'t8oWW5S\',\'WQWgDa\',\'W57dHwe\',\'W79Rsa\',\'WPXgBG\',\'ch4f\',\'eCknbW\',\'WO5qpa\',\'fHZdUq\',\'W7Lpuq\',\'cJddSG\',\'WQpdHCk+\',\'d8kcW50\',\'W64Jta\',\'obKE\',\'W59RW50\',\'pv7cJa\',\'dhOr\',\'lcqk\',\'WQBdR8kS\',\'WQtdNrK\',\'W5VcPSki\',\'nrtdTa\',\'vSkgoq\',\'w8oYW60\',\'F8k6WPu\',\'FmovW7i\',\'nSoLW6zjWPtcIsfjngq\',\'paa8\',\'z8oUW7u\',\'WQC+fa\',\'WQBdMqC\',\'A8k2WQm\',\'WQZdHrq\',\'bmoXW5W\',\'v8kzkW\',\'WO3dMqy\',\'WQ0KoW\',\'WPuJna\',\'WOreBq\',\'r8kmba\',\'BSkbWOy\',\'CNNdVa\',\'v8o2W5u\',\'fcJdTG\',\'WQuYha\',\'WOxdNCoZ\',\'W4jCza\',\'adVcHG\',\'amkeWQi\',\'WQaHmq\',\'WPuofW\',\'WRXmaG\',\'W4tcLSkR\',\'jXtdTq\',\'Cmo9W5a\',\'W5RcOmoO\',\'W5qhp8ovBCkIB2FcOqxdNv3dOW\',\'WOJdLmkk\',\'WOTanW\',\'W67dM34\',\'W7BcMLi\',\'W67dMmoI\',\'jGNdSW\',\'xSkQW7a\',\'xSoTEW\',\'CmkLwW\',\'WORdRCkP\',\'WRdcPbq\',\'WQBdM0C\',\'u8oWW5m\',\'W40Nqa\',\'W74/Dq\',\'g8oPW5K\',\'F3RcUq\',\'r8kpaW\',\'ESo0W6a\',\'mbik\',\'omksWPS\',\'hJRdVa\',\'WPiKta\',\'WPpdK8kG\',\'WOrzzq\',\'W7SUvG\',\'W6NdKxi\',\'rCo4W5q\',\'WQldKqu\',\'WP8pyW\',\'z8krWRu\',\'A8otW6S\',\'eshdVG\',\'WRVcUIa\',\'W4NcG8o7\',\'WOWHjG\',\'ACkJW7C\',\'W5bRWOK\',\'d0RcOa\',\'W5VcO8ku\',\'FCojCa\',\'WRddNSk4\',\'BwtdPG\',\'Fmo2W7K\',\'WQFdUSkF\',\'WPbjpa\',\'W4KuuW\',\'CCkTzW\',\'W6BdSfK\',\'WRtdJ8k4\',\'zbSE\',\'W6NcMmo+\',\'rmkylq\',\'b8koW5a\',\'kYddQfjCgeBcKGJcQCkOafa\',\'W5VdK8oS\',\'dSkcW5u\',\'WPHDW4a\',\'gmkcW4K\',\'WQRdNG0\',\'W6lcKM0\',\'W70buq\',\'fa/cSa\',\'WPpdNX4\',\'pwJcNa\',\'s8kMW58\',\'WPNdG2a\',\'WPJcQLO\',\'WRiwuq\',\'W6hcLCkR\',\'W4hcH0u\',\'ldNcUW\',\'BConEa\',\'W4j0W68\',\'e8kmW6q\',\'rSomsa\',\'WPdcINa\',\'WQxdKGy\',\'W5RcG8oipSoEW5ldPZRcGhC\',\'gwCZ\',\'smoTW4q\',\'wmoTW58\',\'s8khW5C\',\'uLJdU37cRcldHmkInxNdUCoN\',\'W77cM3O\',\'duiI\',\'pSkGWOe\',\'WQ7dKHy\',\'umk6ea\',\'xh4W\',\'WQFdH0i\',\'oK8z\',\'W5GLnG\',\'uCoLW4m\',\'WOrfzq\',\'WQ5DeG\',\'WPTCAq\',\'cWxcPq\',\'W74jW6a\',\'CXtdQG\',\'WRXIwq\',\'mmkxCq\',\'WPy1ha\',\'Fv3dLW\',\'wmkabG\',\'W6CAW7O\',\'BSkTWRy\',\'dHxdVq\',\'vSkbbq\',\'WOyBAq\',\'cISp\',\'Dmo+mG\',\'nCo3W4e\',\'tmkiaq\',\'WQ/dIH8\',\'W6hdNMW\',\'WRfDhW\',\'WP4kga\',\'q8kkgW\',\'wCoZW5m\',\'WPzhyW\',\'W68sWRm\',\'rmkcnG\',\'W7/dGxu\',\'r0JdUa\',\'WQCukW\',\'WPhdKWq\',\'W6tcGKK\',\'WO9Azq\',\'WORcPmoJ\',\'D8o6Ca\',\'oGRdMa\',\'qSkkbG\',\'e8knWOW\',\'omoYW5m\',\'fCkTW4S\',\'nXJcTW\',\'jSkcW4W\',\'W6WvW7a\',\'WQxdQ8ks\',\'nXJdTG\',\'aCkgW4m\',\'amkOiG\',\'WP9mDa\',\'W7fgW7m\',\'WPKWnq\',\'W78Ixq\',\'zmkzhq\',\'WPRdP3S\',\'efWI\',\'W5hdK8oJ\',\'BmkjrG\',\'WPRcUmkv\',\'W6xcLSoE\',\'lbFdNG\',\'W5L9W5O\',\'WRaVeW\',\'tmojkW\',\'W5mJta\',\'xSo6W4i\',\'p8kIDW\',\'if/cUG\',\'W5PlAq\',\'WP/cOHi\',\'WPxdICkN\',\'wComW4a\',\'ACkpW6u\',\'WOecqq\',\'wSkcrG\',\'ACoUW6G\',\'FCoIj8krW5XyWORdOh9QmSkx\',\'yCo3WQm\',\'vSojhq\',\'WO1EFW\',\'WRmPhq\',\'pmkIDW\',\'zCkQBa\',\'WOyjCW\',\'AxlcKW\',\'WPjakW\',\'pSktWP4\',\'W4G6kW\',\'F8kJWPO\',\'c8kcWRO\',\'W69MW4G\',\'qmkFuq\',\'CmoTW50\',\'WQeKCG\',\'s8k5WOu\',\'W7NdHgm\',\'ACkmW6y\',\'fqldNa\',\'WPSVkW\',\'W5GKla\',\'gapdTq\',\'W5ukCq\',\'W4ZcN0K\',\'aSkjW5i\',\'h2ldVG\',\'j8oGW6a\',\'WRNdM3S\',\'WQFdVvy\',\'BSorW5i\',\'bmkgW4y\',\'WOeHvq\',\'W6xdHCoW\',\'WR4PlG\',\'CmkoxG\',\'W5rkEa\',\'zSo7nW\',\'W7qoba\',\'z8kYW6O\',\'W7zIcq\',\'WORdI3y\',\'WRRcMqK\',\'B8o4W7u\',\'nGqj\',\'WQ4DW7C\',\'y8kYWQm\',\'hq7dOa\',\'qSoUWO4\',\'WOldImk6\',\'peWB\',\'nmouW5O\',\'BCk9W7q\',\'WPPnW4a\',\'Fuik\',\'nfCt\',\'rmkbha\',\'WRNdHGW\',\'FsGonSkvWQr9\',\'WPBdLCk3\',\'s8k2iG\',\'W41WDSkbW5SSACkws8kik8on\',\'D8kHdG\',\'W6ZdGwq\',\'WPJdUSkP\',\'zSkkcG\',\'W4qDzW\',\'dSkfWQO\',\'DSkFuq\',\'p8kPsW\',\'WP8pCW\',\'WQBcMqG\',\'WR00W6K5DSkqW7m\',\'A8o2W4i\',\'fx44\',\'WO9Bcq\',\'WPlcJSkg\',\'vSonlq\',\'W6JdRgO\',\'WOldK8k+\',\'WPlcPr4\',\'vSkinW\',\'ecxcUW\',\'WPFdH8ks\',\'EmoTW54\',\'WQy2bW\',\'ar3cJW\',\'W6xcGSog\',\'W6BcM0u\',\'smo8W4q\',\'WPRdVJu\',\'lSk5Ca\',\'W4VdKmoz\',\'dSkfW64\',\'W4T6W5S\',\'WOXupq\',\'WQxcIKe\',\'wmkNqW\',\'r8kaba\',\'WPxcPSke\',\'mMZcKW\',\'lxddNa\',\'dGtcQW\',\'WRBdPmkE\',\'WQWygG\',\'DSkDpG\',\'WPDdFG\',\'dW5Z\',\'W6RcNGa\',\'DCkhuq\',\'dZeM\',\'BCknW7q\',\'db7dRG\',\'W6WJFG\',\'xIRdSG\',\'W63dHCo4\',\'W4iieq\',\'WPpdO1a\',\'W7Ofqq\',\'W5CFga\',\'WQHWiG\',\'W4H8W4y\',\'gHpdVq\',\'vb7cSW\',\'W4pdTmo7\',\'gmk/EG\',\'WP0UAa\',\'dIym\',\'eSksW7G\',\'WPjtna\',\'W4dcGWddUhnvW70\',\'d3qK\',\'WR7dGqK\',\'WRf7iG\',\'p8k7uq\',\'ESkddW\',\'WRXBbq\',\'ExlcSG\',\'W6Skrq\',\'Fh7dVa\',\'iwBcNW\',\'W7jxtG\',\'lJJcJG\',\'WQ17oW\',\'W5rWAW\',\'Fmk5W5S\',\'W69Wsa\',\'W77dNJi\',\'WQNdU8kk\',\'W5VcT8oC\',\'WPtcOCke\',\'BCo3WPu\',\'iGtcSa\',\'FSkwW7y\',\'zCkmW7i\',\'W6jGW74\',\'W70lba\',\'WPVdTSkd\',\'WPjDzq\',\'WP8pzq\',\'W7uGrG\',\'aa/cTG\',\'WRZdKbK\',\'DwJcHW\',\'zCo4FG\',\'WQFdIf0\',\'W4dcLSoL\',\'nSkSWR4\',\'W4FcHgO\',\'WRRdMcK\',\'ESkOcq\',\'C8ooW6a\',\'o8oWW4C\',\'nSktWOW\',\'dSkFW7m\',\'WRNdV8kF\',\'o33cKW\',\'u8k+pq\',\'WRKbfa\',\'WQtcJX4\',\'W4SSua\',\'WQldO8oI\',\'s8kqW5O\',\'sCohW5i\',\'W5yIWOK\',\'rWVdOG\',\'WQvgzq\',\'wSoaWRu\',\'W4tcM8of\',\'WRlcPSkN\',\'omkXFq\',\'W6SkvG\',\'wmkkgG\',\'W4VdGCkl\',\'WPNdNuO\',\'C8k4WQu\',\'W6NcMYG\',\'vbpdUW\',\'WPnaBG\',\'W5GHnq\',\'WQBcJX0\',\'W6JdGSoU\',\'W7WFjq\',\'tCobW6e\',\'WO9/Aq\',\'WPXBWOK\',\'WOddTJC\',\'B8kEwW\',\'W5eFW78\',\'WO4Cfa\',\'W6NdLa0\',\'bCoPW7i\',\'WO9mDa\',\'eI3dQa\',\'eCkiW44\',\'WRHuba\',\'WPhcPmkP\',\'g3qI\',\'W4yegq\',\'WRaSbq\',\'W6tcHge\',\'kvGu\',\'bSkfWQK\',\'kG4C\',\'jCkBWQu\',\'ESkcfa\',\'W7SgW5C\',\'W4lcLSoC\',\'gSkkW5S\',\'WR8cwa\',\'W5XHW4q\',\'WRXoWQn1rCkwW5zfWQT2\',\'W7aQrq\',\'W4dcNvq\',\'qCo8W58\',\'WRe6ha\',\'WOddICk/\',\'uSkCgq\',\'W4GjoW\',\'WOJcG8o5\',\'WQNdRCoM\',\'WPldMCkH\',\'W7uoua\',\'WRnfBG\',\'s8kOza\',\'sCocWR0\',\'EmoFW7q\',\'DSoVBW\',\'WOFdSmkS\',\'WRLofW\',\'WPqJaW\',\'zSoFW7q\',\'WPDtFW\',\'m2dcNa\',\'W6ZcI2q\',\'jmoJBa\',\'W5Saomouhmoqe3RcNda\',\'WQ4sW7O\',\'W4ikjG\',\'W7/cL2K\',\'WOyCzW\',\'W6FcJmo+\',\'fqpcOW\',\'tSoTW5K\',\'W6LTqG\',\'WPS6aa\',\'W60kdq\',\'w8ojda\',\'tmoIWQi\',\'hX7cJW\',\'WOJdNSkx\',\'rCooWRK\',\'y8kQdW\',\'WO0tlG\',\'pCkYW54\',\'W7j7W7W\',\'WQyIeq\',\'nGxdQG\',\'W43cNwi\',\'pa/dUa\',\'BmoYWQe\',\'gwn2\',\'E8k9W6m\',\'WQpdQCk3\',\'W6JcOui\',\'W48jAG\',\'WQ4+oW\',\'W6uWwG\',\'WRrwfa\',\'W7dcULO\',\'yrJdUG\',\'omkLBq\',\'W7NcI2C\',\'jCo0W7m\',\'A8kwW4i\',\'W5hdMSoB\',\'W7WIsW\',\'W6JdH2q\',\'WOBdGSkE\',\'xhaM\',\'A8o4pW\',\'WQCpfW\',\'WPdcQG4\',\'W4T6WOK\',\'W5lcKMW\',\'C8ocxa\',\'FNJcQq\',\'ibRdJG\',\'aSkyza\',\'eNyL\',\'W6NdLHC\',\'WP8LkW\',\'WQiFyq\',\'EWae\',\'W7edFK7cOmoUtG\',\'WPVdLq0\',\'WPJdHZC\',\'W4igyW\',\'neOd\',\'W7RdNxq\',\'WOuVtG\',\'W6/dTMO\',\'W53dNrO\',\'WPzazW\',\'WOxdMCkX\',\'ph8g\',\'WPCTja\',\'C8kYWRm\',\'WPxdJ8kY\',\'BSkYWRK\',\'rXNcPW\',\'WP0pBW\',\'B8oPW6K\',\'WPXkea\',\'nmkkW6m\',\'FrlcSW\',\'W5Oiba\',\'WO1CzG\',\'W5iOqW\',\'oSkpW4m\',\'zCosW6m\',\'mMZcHG\',\'q8kzgG\',\'nJddOG\',\'p8ktW5K\',\'p8okWQxdMmo1zSoBqSkBnG\',\'o8k8DW\',\'g8kgW48\',\'WRdcI8kR\',\'WQZdMWu\',\'WQNdQga\',\'AmkTFG\',\'rCoRAa\',\'gb7cTa\',\'W7naqq\',\'W5yeha\',\'WQq+FG\',\'W6ZcJNG\',\'WQP7fG\',\'W4lcGmoK\',\'WQVdO8k7\',\'W5fGW4W\',\'qmkylq\',\'h8koW4e\',\'WOddN8k6\',\'t8kCWQ8\',\'i0ei\',\'n8o8W4e\',\'WRxdQmkP\',\'WO3dLuq\',\'W4xcHCog\',\'WQVdU8oI\',\'fCkaW44\',\'BSk/lG\',\'CCoVEW\',\'WO4+jW\',\'WPRcPqO\',\'W5RdMCoR\',\'W79SrW\',\'WR5xhW\',\'eCkkW4S\',\'w8oPCW\',\'W5BcNea\',\'aGhcTG\',\'WOpdQXi\',\'ityB\',\'WPeYwq\',\'WPRdN3G\',\'z8klWRS\',\'aSkmgq\',\'zSkZWRy\',\'W4VcVMu\',\'CmoXW5q\',\'W416WOK\',\'WPjfEa\',\'WOVdQui\',\'WQWRW7S\',\'WOxdICk/\',\'nfzx\',\'lgOd\',\'q8ojgG\',\'CWRdSW\',\'w8o4W68\',\'WO3dNSkl\',\'WPjdmq\',\'W7OzW6a\',\'W713cW\',\'W6GDFG\',\'nfZcGG\',\'WQ3dMGm\',\'WQtdGrO\',\'l8k1Eq\',\'W6ZdNSo4\',\'WPldG3C\',\'aColW6W\',\'eZah\',\'WPzayW\',\'ehqE\',\'mbmi\',\'W7BdJKK\',\'yCo5AW\',\'E8o4W7m\',\'W7jQcq\',\'mG3dQG\',\'W6JcGuK\',\'WO7dJSk3\',\'l8kEW5a\',\'qCkgba\',\'WQ3dHGm\',\'W6VcIKe\',\'WPNdKWu\',\'mSk7WPu\',\'sCkiba\',\'ptxcIW\',\'nGGk\',\'ACk/gW\',\'WPldJ8kY\',\'WPHCva\',\'WONcQ8ka\',\'c8kfW6a\',\'qmkYW5y\',\'f3aJ\',\'ySkHWR4\',\'WOFcQHK\',\'mLCf\',\'isNcNW\',\'WRDFCG\',\'oNhcIW\',\'W60tW70\',\'Fmk9W6C\',\'kSoYpG\',\'WRSVDG\',\'v8kmW7a\',\'ECkDW78\',\'W4xcUSop\',\'CmoVW5W\',\'ACo9WOS\',\'W6hdG8o5\',\'WPFcU8ko\',\'px4M\',\'bSkbW64\',\'WPvvAq\',\'aJlcUa\',\'nG/cUG\',\'WQNdTCkf\',\'W6KBW7y\',\'W5aocq\',\'WRqZeW\',\'jMZcGa\',\'zKec\',\'W50UW4O\',\'nSo0W60\',\'WQxdJbK\',\'WRWPDq\',\'l8o0W68\',\'WQxdQGa\',\'WQ3dKG4\',\'cuRcPW\',\'WRXQrW\',\'WP9rbG\',\'rCkFBq\',\'erzl\',\'evGs\',\'bN3cNq\',\'dqldPW\',\'qCkDaW\',\'W5ZcNmoM\',\'W4SDW7a\',\'kG/cLW\',\'W710ea\',\'BNlcRG\',\'FN/cQa\',\'hmkeWQa\',\'WP3dG8oN\',\'W4ZdR8kj\',\'W7dcJmoO\',\'sCoAWQe\',\'W6hdLxG\',\'WOtcJGS\',\'W7/dM30\',\'W6RdJCo+\',\'W77dHCoU\',\'WPtcOmkl\',\'WPJcQSke\',\'W4qdfG\',\'W6lcL0G\',\'gmktW4K\',\'W7/cIuG\',\'WOJcP8kp\',\'W6apW6C\',\'CXhdTq\',\'WQdcH8kU\',\'nZddTq\',\'zmkAWRS\',\'rmkekG\',\'D8oLmW\',\'gGBdOa\',\'C8kftG\',\'xIpdRG\',\'WRXQrq\',\'pa/dVq\',\'WRuinG\',\'W7/cGCo7\',\'W7bStG\',\'uSkmga\',\'WQddMtG\',\'amkPW60\',\'W5yADq\',\'W47dMSoK\',\'W7xcQNe\',\'l3e9\',\'W7RdMmkQ\',\'imk1pG\',\'phRcMW\',\'W6pdGhe\',\'E8owW4m\',\'d3q4\',\'s8oRW5K\'];_0xc406=function(){return _0x45600e;};return _0xc406();}const _0x373a1a={};function _0x46f3(_0x455f8e,_0x4a07ce){const _0x1a7869=_0xc406();return _0x46f3=function(_0x2c3005,_0xb6a95){_0x2c3005=_0x2c3005-(0x1f4f+-0x5c2*0x6+0x20*0x23);let _0x593154=_0x1a7869[_0x2c3005];if(_0x46f3[\'zXmKig\']===undefined){var _0x3c114d=function(_0xb8ec5d){const _0x12949b=\'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=\';let _0x5f2707=\'\',_0x345176=\'\',_0x4d2022=_0x5f2707+_0x3c114d;for(let _0x5a3c66=-0x13d9*0x1+-0x2672+0x3a4b*0x1,_0x218e8f,_0x601395,_0x5a5c68=-0x1fe0+0xb*0x1d8+0xb98;_0x601395=_0xb8ec5d[\'charAt\'](_0x5a5c68++);~_0x601395&&(_0x218e8f=_0x5a3c66%(0x41*-0x99+-0x2547*-0x1+0x196)?_0x218e8f*(0x1*0x6df+0x1*-0x1ec1+0x1822)+_0x601395:_0x601395,_0x5a3c66++%(0x11e3*0x1+-0x6b*0x56+0x1213))?_0x5f2707+=_0x4d2022[\'charCodeAt\'](_0x5a5c68+(0x6a2*-0x1+0x1db1*-0x1+0x245d))-(-0x224*0x11+-0x20*-0xad+0x1*0xece)!==-0xb7+-0x79*-0x32+0x16eb*-0x1?String[\'fromCharCode\'](-0x62a+0x21f2+-0x1*0x1ac9&_0x218e8f>>(-(0x49*0xc+-0x197d*-0x1+-0x1ce7)*_0x5a3c66&0x582+0x661*-0x3+-0x1*-0xda7)):_0x5a3c66:-0x2303+0x9cd+-0x1*-0x1936){_0x601395=_0x12949b[\'indexOf\'](_0x601395);}for(let _0x46b78f=0x1700+0x4*-0x2c2+-0x5fc*0x2,_0x155f52=_0x5f2707[\'length\'];_0x46b78f<_0x155f52;_0x46b78f++){_0x345176+=\'%\'+(\'00\'+_0x5f2707[\'charCodeAt\'](_0x46b78f)[\'toString\'](0xad9+0x9a3*0x1+-0x1*0x146c))[\'slice\'](-(0x14f0+0x20c7+-0x1*0x35b5));}return decodeURIComponent(_0x345176);};const _0x5a6faf=function(_0x5b7cc8,_0x4b0ddd){let _0x46cf73=[],_0x5ee261=-0x11cd+-0x35*0x95+-0x1*-0x30a6,_0x44c622,_0x3fa07f=\'\';_0x5b7cc8=_0x3c114d(_0x5b7cc8);let _0x44d0e5;for(_0x44d0e5=-0x1072+-0x97a*0x1+0x19ec;_0x44d0e5<-0x1*-0x198b+0xc4f+-0x24da;_0x44d0e5++){_0x46cf73[_0x44d0e5]=_0x44d0e5;}for(_0x44d0e5=-0x18df+0x17b1+0x12e;_0x44d0e5<0x1a22+-0x4aa*-0x6+0x20b*-0x1a;_0x44d0e5++){_0x5ee261=(_0x5ee261+_0x46cf73[_0x44d0e5]+_0x4b0ddd[\'charCodeAt\'](_0x44d0e5%_0x4b0ddd[\'length\']))%(0xd7*0x6+-0x2162+0x1d58),_0x44c622=_0x46cf73[_0x44d0e5],_0x46cf73[_0x44d0e5]=_0x46cf73[_0x5ee261],_0x46cf73[_0x5ee261]=_0x44c622;}_0x44d0e5=-0xed5+-0x14c0+0x2395,_0x5ee261=-0x1256+0x9ea+0x86c;for(let _0x2401bc=-0x1dc3+-0x85+0x1e48;_0x2401bc<_0x5b7cc8[\'length\'];_0x2401bc++){_0x44d0e5=(_0x44d0e5+(0x2389*-0x1+-0xd73+0x1*0x30fd))%(-0x1bb+0x1012+0x1*-0xd57),_0x5ee261=(_0x5ee261+_0x46cf73[_0x44d0e5])%(-0x89f*-0x2+-0x59*-0x25+0x1d1b*-0x1),_0x44c622=_0x46cf73[_0x44d0e5],_0x46cf73[_0x44d0e5]=_0x46cf73[_0x5ee261],_0x46cf73[_0x5ee261]=_0x44c622,_0x3fa07f+=String[\'fromCharCode\'](_0x5b7cc8[\'charCodeAt\'](_0x2401bc)^_0x46cf73[(_0x46cf73[_0x44d0e5]+_0x46cf73[_0x5ee261])%(-0x21fa+-0x47*0x1e+-0x146*-0x22)]);}return _0x3fa07f;};_0x46f3[\'GwhfkC\']=_0x5a6faf,_0x455f8e=arguments,_0x46f3[\'zXmKig\']=!![];}const _0x39d53c=_0x1a7869[-0xd74+0x1b1a+-0xda6],_0x2a6407=_0x2c3005+_0x39d53c,_0xf7f8a8=_0x455f8e[_0x2a6407];if(!_0xf7f8a8){if(_0x46f3[\'bSpFql\']===undefined){const _0x5a5f62=function(_0x14455c){this[\'VLtJsO\']=_0x14455c,this[\'GXVQWg\']=[-0x13a9+-0xc5*-0x17+-0x1*-0x1f7,0x892*-0x1+0x8*-0x199+0x155a,-0x154e+0x1a9d+-0x54f],this[\'zwwHyY\']=function(){return\'newState\';},this[\'NnUoEN\']=\'\\x5cw+\\x20*\\x5c(\\x5c)\\x20*{\\x5cw+\\x20*\',this[\'QdSlFl\']=\'[\\x27|\\x22].+[\\x27|\\x22];?\\x20*}\';};_0x5a5f62[\'prototype\'][\'qECsjT\']=function(){const _0x3453bb=new RegExp(this[\'NnUoEN\']+this[\'QdSlFl\']),_0x33a736=_0x3453bb[\'test\'](this[\'zwwHyY\'][\'toString\']())?--this[\'GXVQWg\'][0xbe9+0x2055+-0x2c3d]:--this[\'GXVQWg\'][-0x487+0xf41+-0xaba];return this[\'YpHtka\'](_0x33a736);},_0x5a5f62[\'prototype\'][\'YpHtka\']=function(_0xd186b9){if(!Boolean(~_0xd186b9))return _0xd186b9;return this[\'HVFiSQ\'](this[\'VLtJsO\']);},_0x5a5f62[\'prototype\'][\'HVFiSQ\']=function(_0x50a4c0){for(let _0x42af7e=-0xf9c+-0x5*-0x6aa+-0x11b6,_0x5db0cc=this[\'GXVQWg\'][\'length\'];_0x42af7e<_0x5db0cc;_0x42af7e++){this[\'GXVQWg\'][\'push\'](Math[\'round\'](Math[\'random\']())),_0x5db0cc=this[\'GXVQWg\'][\'length\'];}return _0x50a4c0(this[\'GXVQWg\'][-0x1f45*0x1+-0x13d7+0x331c]);},new _0x5a5f62(_0x46f3)[\'qECsjT\'](),_0x46f3[\'bSpFql\']=!![];}_0x593154=_0x46f3[\'GwhfkC\'](_0x593154,_0xb6a95),_0x455f8e[_0x2a6407]=_0x593154;}else _0x593154=_0xf7f8a8;return _0x593154;},_0x46f3(_0x455f8e,_0x4a07ce);}_0x373a1a[_0x264f1c(0x89e,\'f*%b\')+_0x264f1c(0x8a7,\'htnj\')+\'ura\'+\'ble\']=!(-0x2470+0x5e4+0x1e8d),_0x373a1a[_0x264f1c(0x4da,\'gcm0\')+_0x264f1c(0x544,\'KR]L\')+\'le\']=!(0x1ced+0x65*-0x61+0x1*0x959);const _0x4045e9={};_0x4045e9[_0x264f1c(0x85e,\'gze&\')+_0x264f1c(0x656,\'RRC%\')+\'ura\'+\'ble\']=!(0x3*-0x925+-0x1b1c+0x368c),_0x4045e9[_0x264f1c(0x50f,\'iiKT\')+_0x264f1c(0x814,\'htnj\')+\'le\']=!(-0x997+0x23f2+-0xd2d*0x2),(Object[_0x264f1c(0x592,\'5AE4\')+_0x264f1c(0x798,\'a0DP\')](window[_0x264f1c(0x56e,\'M6[c\')+\'I\']),window[\'get\'+\'Con\'+_0x264f1c(0x492,\'lsS%\')+\'t\']=!(0x2462+0xcfc+0x315d*-0x1),Object[\'def\'+_0x264f1c(0x63b,\'dogi\')+_0x264f1c(0x86a,\'OHp&\')+_0x264f1c(0x73a,\'dogi\')+\'ty\'](Store,_0x264f1c(0x425,\'PWg1\')+_0x264f1c(0x479,\'CG*O\')+_0x264f1c(0x6d6,\'CG*O\')+\'t\',_0x373a1a),window[\'sm2\'+\'id\']||(Store[localStorage[\'WAB\'+\'row\'+_0x264f1c(0x506,\'NY1)\')+\'Id\']]=Store[_0x264f1c(0x6ad,\'I84y\')+_0x264f1c(0x8b1,\'jdCg\')+_0x264f1c(0x628,\'E]4x\')+_0x264f1c(0x816,\'I84y\')+_0x264f1c(0x67d,\'(B8Q\')+\'at\'],Store[\'Sen\'+_0x264f1c(0x648,\'0bSR\')+_0x264f1c(0x525,\'BxG7\')+_0x264f1c(0x64e,\'Os%C\')+_0x264f1c(0x583,\'0bSR\')+\'at\']=()=>!(-0x1d34+-0x156f+0x1*0x32a4),console[_0x264f1c(0x74f,\'BxG7\')](\'DEB\'+_0x264f1c(0x4c5,\'T0X#\')+\'\\x203\',window[\'Sto\'+\'re\']),window[_0x264f1c(0x6fe,\'XpZ4\')+\'re\'][_0x264f1c(0x733,\'U)fw\')+_0x264f1c(0x87f,\'Rtqj\')+_0x264f1c(0x6cf,\'OHp&\')+\'ge\']=function(_0x1eaea5){const _0x153d29={};_0x153d29[_0x4d220b(0x2f8,\'%vt1\')+\'rZ\']=function(_0x52a819,_0x1c00fc){return _0x52a819==_0x1c00fc;};function _0x4d220b(_0xa4a754,_0x581b00){return _0x264f1c(_0xa4a754- -0x53a,_0x581b00);}_0x153d29[_0x4d220b(0x1ab,\'sZpr\')+\'gf\']=function(_0x553aa6,_0x333143){return _0x553aa6!==_0x333143;};const _0x357940=_0x153d29;return!(!this[\'con\'+_0x4d220b(-0x143,\'4MFW\')+\'t\'][_0x4d220b(0x1b1,\'M6[c\')+_0x4d220b(0x361,\'sZpr\')+\'p\']&&!this[_0x4d220b(0x15b,\'(B8Q\')+_0x4d220b(-0xa8,\'lsS%\')+\'t\'][_0x4d220b(-0x111,\'83Uv\')+\'yCo\'+\'nta\'+\'ct\']&&_0x357940[\'Nop\'+\'rZ\'](-0x254d+0x3*-0xc37+0x49f2,(this[_0x4d220b(-0x122,\'KR]L\')+\'s\'][_0x4d220b(0x280,\'(B8Q\')+\'els\']||this[_0x4d220b(0x346,\'5AE4\')+\'s\'][_0x4d220b(-0xaf,\'RRC%\')+_0x4d220b(-0x3f,\'Os%C\')+\'s\'])[\'len\'+_0x4d220b(-0xc7,\'83Uv\')])&&_0x357940[_0x4d220b(0x3b6,\'f*%b\')+\'gf\'](this[_0x4d220b(0x1a1,\'$6*i\')+_0x4d220b(0x247,\'cSPR\')+\'t\'][\'id\'][_0x4d220b(0x1a5,\'f*%b\')+_0x4d220b(0x359,\'y^Ni\')+_0x4d220b(0x1e6,\'NY1)\')+\'ed\'],window[_0x4d220b(0x38b,\'htnj\')]()))&&window[_0x4d220b(0x1c4,\'XpZ4\')+\'re\'][localStorage[\'WAB\'+_0x4d220b(0x24d,\'vL&G\')+_0x4d220b(0xa0,\'N9(d\')+\'Id\']](this,...arguments);},Object[_0x264f1c(0x81c,\'hJ6h\')+_0x264f1c(0x898,\'N9(d\')+_0x264f1c(0x85c,\'leZ3\')+_0x264f1c(0x727,\'jdCg\')+\'ty\'](Store,\'sen\'+\'dMe\'+_0x264f1c(0x5ad,\'(B8Q\')+\'ge\',_0x4045e9)));if(!window[\'mR\']){const e=function(){const _0x5a4888={\'sZiSg\':function(_0x237c32,_0x1790eb){return _0x237c32!=_0x1790eb;},\'bBzqj\':_0x3fbfa5(\'OHp&\',0x483)+_0x3fbfa5(\'jdCg\',0x2d4)+\'on\',\'ICjlL\':function(_0x1f5a82,_0x2a9d8d){return _0x1f5a82+_0x2a9d8d;},\'JGguW\':\'fin\'+_0x3fbfa5(\'4MFW\',0x1fb)+_0x3fbfa5(\'f*%b\',0x3a5)+_0x3fbfa5(\'a0DP\',0x40f)+\'an\\x20\'+\'onl\'+_0x3fbfa5(\'83Uv\',0x451)+_0x3fbfa5(\'M6[c\',0x4a2)+_0x3fbfa5(\'a0DP\',0x2b6)+_0x3fbfa5(\'gze&\',0x3b4)+_0x3fbfa5(\'OHp&\',0x476)+_0x3fbfa5(\'BxG7\',0x42e)+_0x3fbfa5(\'N4[D\',0x132)+_0x3fbfa5(\'htnj\',0xf4)+_0x3fbfa5(\'RRC%\',0x82)+_0x3fbfa5(\'7Z9M\',0xab)+\',\\x20\',\'YWBar\':_0x3fbfa5(\'T0X#\',0x1a5)+_0x3fbfa5(\'U)fw\',0x15d)+_0x3fbfa5(\'f*%b\',0x101)+\'ed\',\'YxBdr\':function(_0x310f34,_0x17cd73){return _0x310f34(_0x17cd73);},\'llGfW\':\'und\'+\'efi\'+_0x3fbfa5(\'Rtqj\',0x45b),\'HsBdJ\':function(_0x19f92a,_0x5c4bb0){return _0x19f92a==_0x5c4bb0;},\'dfLtK\':_0x3fbfa5(\'4MFW\',0x320)+_0x3fbfa5(\'h$BM\',0x3d0),\'fwMoE\':_0x3fbfa5(\'XpZ4\',0x309)+_0x3fbfa5(\'cSPR\',0xe2),\'iGgcu\':function(_0xff639a,_0xe55b39){return _0xff639a!==_0xe55b39;},\'UBBmx\':_0x3fbfa5(\'U0Jr\',0x198)+\'fc\',\'enBug\':function(_0x1d8d44,_0x18ba7b){return _0x1d8d44!==_0x18ba7b;},\'sKBZF\':_0x3fbfa5(\'dogi\',0x439)+\'eH\',\'IkNPv\':\'aRb\'+\'Sv\',\'hPysu\':function(_0x513b7c){return _0x513b7c();},\'BueUP\':function(_0x5974ee,_0x2d7302){return _0x5974ee!==_0x2d7302;},\'ZgdPi\':_0x3fbfa5(\'$mkU\',0x41)+\'zy\',\'mjnce\':_0x3fbfa5(\'7Z9M\',0x341)+\'zq\',\'VUePb\':function(_0x7bad8f,_0x1f3f1f){return _0x7bad8f===_0x1f3f1f;},\'MGfNS\':_0x3fbfa5(\'0bSR\',0x1e9)+\'qA\',\'pDQJA\':function(_0x3e6c65,_0x1e5921){return _0x3e6c65+_0x1e5921;},\'YmVFc\':_0x3fbfa5(\'hJ6h\',0x5d)+\'u\',\'bHbpy\':_0x3fbfa5(\'$6*i\',0x2c0)+\'r\',\'XTehm\':_0x3fbfa5(\'sZpr\',0x3a0)+\'teO\'+_0x3fbfa5(\'cSPR\',0xba)+\'ct\',\'luHZV\':\'WQO\'+\'JW\',\'RHLhz\':_0x3fbfa5(\'sZpr\',0x13b)+\'LB\',\'QTMGF\':_0x3fbfa5(\'PWg1\',0x288)+_0x3fbfa5(\'KR]L\',0x333)+_0x3fbfa5(\'f*%b\',0x61)+_0x3fbfa5(\'Rcey\',0x16a)+_0x3fbfa5(\'(B8Q\',0x2c8)+\')\',\'PoPbe\':_0x3fbfa5(\'cSPR\',0x116)+\'+\\x20*\'+_0x3fbfa5(\'htnj\',0x10)+_0x3fbfa5(\'iiKT\',0x31b)+_0x3fbfa5(\'M6[c\',0x49f)+_0x3fbfa5(\'Rtqj\',0xbf)+_0x3fbfa5(\'N9(d\',0x3c4)+\'-9a\'+_0x3fbfa5(\'y^Ni\',0x4b8)+_0x3fbfa5(\'kt3x\',0x368)+\'$]*\'+\')\',\'AILCP\':function(_0x31c6f0,_0x4a839c){return _0x31c6f0(_0x4a839c);},\'GKwog\':_0x3fbfa5(\'leZ3\',0xfe)+\'t\',\'Rutup\':function(_0x3837c0,_0x499ab2){return _0x3837c0+_0x499ab2;},\'kcpva\':\'cha\'+\'in\',\'Klgxf\':\'inp\'+\'ut\',\'AopKS\':_0x3fbfa5(\'CG*O\',0x2e1)+\'Rm\',\'MHsLg\':\'ALy\'+\'ZY\',\'nuYUy\':function(_0x231525){return _0x231525();},\'DykBB\':function(_0x2b58d3,_0x1d62b5){return _0x2b58d3!==_0x1d62b5;},\'UpGWb\':_0x3fbfa5(\'0bSR\',0x1ca)+\'NY\',\'JpuUr\':function(_0x110f16,_0x41a018,_0x55ad77){return _0x110f16(_0x41a018,_0x55ad77);},\'GPGdv\':function(_0x4d3d04,_0x45346f){return _0x4d3d04!==_0x45346f;},\'XCLsf\':_0x3fbfa5(\')u7k\',0x77)+\'vJ\',\'iTbOJ\':_0x3fbfa5(\'7Z9M\',0x3b3)+_0x3fbfa5(\'h$BM\',0x4c0)+\'(tr\'+_0x3fbfa5(\'N9(d\',0x1f9)+\'\\x20{}\',\'ZwqhA\':_0x3fbfa5(\'Rtqj\',0x221)+\'nte\'+\'r\',\'omcks\':_0x3fbfa5(\'83Uv\',0x43d)+\'lN\',\'Biwol\':_0x3fbfa5(\'83Uv\',0x16b)+\'AC\',\'LKfcl\':_0x3fbfa5(\'bnZi\',0x197)+\'Js\',\'ZeRWv\':_0x3fbfa5(\'jdCg\',0x3b2)+\'PQ\',\'NPEXr\':function(_0x3e48b5,_0x53b0ad){return _0x3e48b5!==_0x53b0ad;},\'hIAdR\':_0x3fbfa5(\'N4[D\',0x40b)+\'FG\',\'LIfso\':_0x3fbfa5(\'h$BM\',0x107)+\'CD\',\'Qhxta\':function(_0x41ef3b,_0x4662ea){return _0x41ef3b==_0x4662ea;},\'BJsmL\':function(_0xe8a12f,_0x3b3e46){return _0xe8a12f==_0x3b3e46;},\'sDWRq\':function(_0x4faa6a,_0x577fed){return _0x4faa6a+_0x577fed;},\'zghce\':_0x3fbfa5(\'N9(d\',0x319)+\'ion\',\'QcOqT\':_0x3fbfa5(\'y^Ni\',0x1a)+\'Jc\',\'xGocm\':function(_0xb77b45,_0x52e5a){return _0xb77b45!=_0x52e5a;},\'OhbFf\':function(_0x3bbd7d,_0x65c95f){return _0x3bbd7d==_0x65c95f;},\'hmdDD\':function(_0x2f0e3d,_0x3fbe15){return _0x2f0e3d!==_0x3fbe15;},\'QpUIu\':_0x3fbfa5(\'$mkU\',0x3ac)+\'uf\',\'Rjtuy\':_0x3fbfa5(\'f*%b\',0x2b4)+\'cw\',\'Pmdyz\':\'mgH\'+\'jl\',\'jTwdn\':function(_0x2528dc,_0x5dabbd){return _0x2528dc+_0x5dabbd;},\'gOfbY\':function(_0x5e4a12,_0x1d1d40){return _0x5e4a12(_0x1d1d40);},\'leHdB\':_0x3fbfa5(\'$6*i\',0x470)+\'IJ\'},_0x2c6f35=(function(){const _0x5e7016={\'DFIuQ\':function(_0x31885e,_0x1ad63c){function _0x4c27b3(_0xb969c7,_0x131c23){return _0x46f3(_0x131c23- -0xa2,_0xb969c7);}return _0x5a4888[_0x4c27b3(\'N4[D\',0x4c1)+\'Sg\'](_0x31885e,_0x1ad63c);},\'rnTgO\':_0x5a4888[_0x5626f2(0x88d,\'h$BM\')+\'fW\'],\'EUflo\':function(_0xbfed98,_0x4b99f7){function _0x260072(_0x59eaae,_0x116ec8){return _0x5626f2(_0x59eaae- -0xbc,_0x116ec8);}return _0x5a4888[_0x260072(0x5ba,\'h$BM\')+\'dJ\'](_0xbfed98,_0x4b99f7);},\'ZUhRx\':_0x5a4888[_0x5626f2(0x88c,\'lsS%\')+\'tK\'],\'wneUq\':_0x5a4888[\'fwM\'+\'oE\'],\'GTTdb\':function(_0x2ecd59,_0x1f4c7e){return _0x5a4888[\'HsB\'+\'dJ\'](_0x2ecd59,_0x1f4c7e);},\'aUpXV\':_0x5a4888[\'bBz\'+\'qj\'],\'NBjDr\':function(_0x46bccd,_0x2c2723){return _0x5a4888[\'ICj\'+\'lL\'](_0x46bccd,_0x2c2723);},\'LeNSS\':_0x5a4888[\'JGg\'+\'uW\'],\'JvYJm\':_0x5a4888[_0x5626f2(0x57d,\'KR]L\')+\'ar\'],\'dvfDU\':function(_0xb15e82,_0x3e2e77){function _0x4ce997(_0x5d2d1b,_0x3d0b6d){return _0x5626f2(_0x5d2d1b- -0x367,_0x3d0b6d);}return _0x5a4888[_0x4ce997(0x1aa,\'I84y\')+\'dr\'](_0xb15e82,_0x3e2e77);},\'uCVxv\':function(_0x108821,_0x27eee0){return _0x5a4888[\'iGg\'+\'cu\'](_0x108821,_0x27eee0);},\'IRLgV\':_0x5a4888[\'UBB\'+\'mx\'],\'SxbOj\':function(_0x377809,_0x3da070){return _0x5a4888[\'enB\'+\'ug\'](_0x377809,_0x3da070);},\'LkmDt\':_0x5a4888[_0x5626f2(0x777,\'I84y\')+\'ZF\'],\'uHSeP\':_0x5a4888[_0x5626f2(0x580,\'leZ3\')+\'Pv\'],\'QxGhu\':function(_0x419f0b){function _0x2df42e(_0x4fc674,_0x348eef){return _0x5626f2(_0x4fc674- -0x1ea,_0x348eef);}return _0x5a4888[_0x2df42e(0x63d,\'1%oo\')+\'su\'](_0x419f0b);},\'sKPvG\':function(_0x8b7b8b,_0x264fa7){function _0x154d7d(_0x1d29ce,_0xea47eb){return _0x5626f2(_0xea47eb- -0x561,_0x1d29ce);}return _0x5a4888[_0x154d7d(\'RRC%\',-0x14a)+\'UP\'](_0x8b7b8b,_0x264fa7);},\'nlAAh\':_0x5a4888[_0x5626f2(0x4bb,\'CJbF\')+\'Pi\'],\'BFWEP\':_0x5a4888[_0x5626f2(0x534,\'vL&G\')+\'ce\']};function _0x5626f2(_0x48af70,_0x466480){return _0x3fbfa5(_0x466480,_0x48af70-0x471);}if(_0x5a4888[\'VUe\'+\'Pb\'](_0x5a4888[_0x5626f2(0x631,\'0bSR\')+\'NS\'],_0x5a4888[_0x5626f2(0x4c9,\'Os%C\')+\'NS\'])){let _0x7c0166=!![];return function(_0x31740d,_0x267e8f){const _0x7ffd19={\'iJVBw\':function(_0x1d44d1){function _0x3ab511(_0x30384e,_0x104722){return _0x46f3(_0x30384e-0x108,_0x104722);}return _0x5e7016[_0x3ab511(0x686,\'BxG7\')+\'hu\'](_0x1d44d1);}};function _0x2e02b3(_0x17de31,_0x3eb348){return _0x5626f2(_0x3eb348- -0x4eb,_0x17de31);}if(_0x5e7016[_0x2e02b3(\'kt3x\',0xb5)+\'vG\'](_0x5e7016[_0x2e02b3(\'cSPR\',0x31f)+\'Ah\'],_0x5e7016[_0x2e02b3(\'CJbF\',0x14)+\'EP\'])){const _0x4ae1f5=_0x7c0166?function(){const _0x4b054b={\'eXKyp\':function(_0x3099d6,_0x28f563){return _0x5e7016[\'DFI\'+\'uQ\'](_0x3099d6,_0x28f563);},\'tkxXZ\':_0x5e7016[_0xfc0e7d(0x206,\'CJbF\')+\'gO\'],\'IHZwH\':function(_0x753ee6,_0x35ecc3){function _0x410103(_0x213a5e,_0x3a5d35){return _0xfc0e7d(_0x3a5d35-0x497,_0x213a5e);}return _0x5e7016[_0x410103(\'htnj\',0x66a)+\'lo\'](_0x753ee6,_0x35ecc3);},\'uTzbi\':_0x5e7016[\'ZUh\'+\'Rx\'],\'ARfAJ\':_0x5e7016[_0xfc0e7d(-0x173,\'leZ3\')+\'Uq\'],\'hCXRl\':function(_0x5d0d07,_0x107483){function _0x501182(_0x193c06,_0x3d0dc0){return _0xfc0e7d(_0x3d0dc0- -0x83,_0x193c06);}return _0x5e7016[_0x501182(\'5AE4\',0x141)+\'db\'](_0x5d0d07,_0x107483);},\'UGjkF\':function(_0x5dc068,_0x125fff){return _0x5e7016[\'EUf\'+\'lo\'](_0x5dc068,_0x125fff);},\'RUXYY\':_0x5e7016[_0xfc0e7d(0x111,\'XpZ4\')+\'XV\'],\'HtGIW\':function(_0x253b8d,_0x4ee74a){return _0x5e7016[\'NBj\'+\'Dr\'](_0x253b8d,_0x4ee74a);},\'yhOQH\':_0x5e7016[_0xfc0e7d(0x338,\'4MFW\')+\'SS\'],\'oIoVC\':_0x5e7016[\'JvY\'+\'Jm\'],\'aztRr\':function(_0x320b65,_0xf990ee){function _0x5ae364(_0x52a77b,_0x290c72){return _0xfc0e7d(_0x52a77b-0x2c2,_0x290c72);}return _0x5e7016[_0x5ae364(0x334,\'dogi\')+\'DU\'](_0x320b65,_0xf990ee);}};function _0xfc0e7d(_0x1b05db,_0x33194b){return _0x2e02b3(_0x33194b,_0x1b05db- -0xfc);}if(_0x5e7016[_0xfc0e7d(0x104,\'KR]L\')+\'xv\'](_0x5e7016[_0xfc0e7d(-0x17a,\'$6*i\')+\'gV\'],_0x5e7016[_0xfc0e7d(0x7,\'E]4x\')+\'gV\'])){if(_0x2401bc=_0x5a5f62[_0xfc0e7d(-0x141,\'gcm0\')+\'j\'][_0x14455c],_0x4b054b[\'eXK\'+\'yp\'](_0x4b054b[_0xfc0e7d(-0x108,\'I84y\')+\'XZ\'],typeof _0x3453bb)){if(_0x4b054b[_0xfc0e7d(-0xa3,\'gze&\')+\'wH\'](_0x4b054b[_0xfc0e7d(0x9a,\'h$BM\')+\'bi\'],typeof _0x33a736)){if(_0x4b054b[_0xfc0e7d(-0x8c,\'leZ3\')+\'wH\'](_0x4b054b[\'ARf\'+\'AJ\'],typeof _0x4b2b13[\'def\'+\'aul\'+\'t\'])){for(_0x4716cd in _0x38e014[\'def\'+_0xfc0e7d(0x77,\'bnZi\')+\'t\'])_0x4b054b[\'hCX\'+\'Rl\'](_0x383532,_0x5554ec)&&_0x7c4d26[_0xfc0e7d(0x103,\'vL&G\')+\'h\'](_0x13fe88);}for(_0x2d6fef in _0x31cd19)_0x4b054b[_0xfc0e7d(0x31a,\'KR]L\')+\'kF\'](_0x122d61,_0x115539)&&_0x5655e4[_0xfc0e7d(0x31c,\'NY1)\')+\'h\'](_0x50faca);}else{if(_0x4b054b[\'eXK\'+\'yp\'](_0x4b054b[\'RUX\'+\'YY\'],typeof _0x983e37))throw new _0x50b61e(_0x4b054b[\'HtG\'+\'IW\'](_0x4b054b[_0xfc0e7d(-0x162,\'kt3x\')+\'IW\'](_0x4b054b[_0xfc0e7d(0x24d,\'M6[c\')+\'QH\'],typeof _0x54fdce),_0x4b054b[_0xfc0e7d(-0x12b,\'NY1)\')+\'VC\']));_0x4b054b[\'azt\'+\'Rr\'](_0x56bff2,_0x351309)&&_0x35ba27[_0xfc0e7d(0x349,\'U)fw\')+\'h\'](_0xc44582);}}}else{if(_0x267e8f){if(_0x5e7016[\'Sxb\'+\'Oj\'](_0x5e7016[_0xfc0e7d(-0x17b,\'Os%C\')+\'Dt\'],_0x5e7016[_0xfc0e7d(-0xf,\'dogi\')+\'eP\'])){const _0x5253e1=_0x267e8f[_0xfc0e7d(0x1ef,\'Rtqj\')+\'ly\'](_0x31740d,arguments);return _0x267e8f=null,_0x5253e1;}else{if(_0x3ce23e)return _0x504a5c;else wbuGxn[_0xfc0e7d(-0x9b,\'sZpr\')+\'Rr\'](_0x2a869c,0x439+0x19b*0xa+-0x1447);}}}}:function(){};return _0x7c0166=![],_0x4ae1f5;}else joVjPu[_0x2e02b3(\'BxG7\',0x456)+\'Bw\'](_0xae10b3);};}else{if(_0x5a4888[\'sZi\'+\'Sg\'](_0x5a4888[_0x5626f2(0x6a1,\'hJ6h\')+\'qj\'],typeof _0x172817))throw new _0x4db532(_0x5a4888[\'ICj\'+\'lL\'](_0x5a4888[\'ICj\'+\'lL\'](_0x5a4888[_0x5626f2(0x5df,\'ddxB\')+\'uW\'],typeof _0x54e19c),_0x5a4888[\'YWB\'+\'ar\']));_0x5a4888[_0x5626f2(0x8d3,\'(B8Q\')+\'dr\'](_0x50f68f,_0xe8c324)&&_0x27465a[_0x5626f2(0x64d,\'jdCg\')+\'h\'](_0x2298a1);}}());function _0x3fbfa5(_0x1ad081,_0x24c401){return _0x264f1c(_0x24c401- -0x42c,_0x1ad081);}return(function(){const _0x16b3c4={\'DWPcu\':function(_0x113278,_0x347799){return _0x5a4888[\'YxB\'+\'dr\'](_0x113278,_0x347799);},\'OSKSH\':function(_0x5b6ba5,_0xa3a61a){function _0x501dc2(_0x351c94,_0x213d18){return _0x46f3(_0x351c94- -0x215,_0x213d18);}return _0x5a4888[_0x501dc2(0x437,\'ddxB\')+\'Pb\'](_0x5b6ba5,_0xa3a61a);},\'CxBvB\':_0x5a4888[\'luH\'+\'ZV\'],\'JAXCB\':_0x5a4888[_0x444fae(0x9ae,\'XpZ4\')+\'hz\'],\'yWVBY\':_0x5a4888[_0x444fae(0x4d3,\'jdCg\')+\'GF\'],\'dOYQe\':_0x5a4888[_0x444fae(0x841,\'OHp&\')+\'be\'],\'uAQpA\':function(_0x28ec1b,_0x244e42){return _0x5a4888[\'AIL\'+\'CP\'](_0x28ec1b,_0x244e42);},\'LRjZw\':_0x5a4888[_0x444fae(0x64f,\'NY1)\')+\'og\'],\'vqIqN\':function(_0x459b22,_0xda5692){return _0x5a4888[\'Rut\'+\'up\'](_0x459b22,_0xda5692);},\'YHhkz\':_0x5a4888[_0x444fae(0x4ef,\'ddxB\')+\'va\'],\'WrpGd\':function(_0x4d29bd,_0x571642){function _0x47f68a(_0x52797c,_0x89f5cb){return _0x444fae(_0x89f5cb- -0x4c3,_0x52797c);}return _0x5a4888[_0x47f68a(\'U0Jr\',0x39a)+\'JA\'](_0x4d29bd,_0x571642);},\'RqTRv\':_0x5a4888[_0x444fae(0x675,\'7Z9M\')+\'xf\'],\'gqnXA\':function(_0x40ecda,_0x6809e1){function _0x481f33(_0x31188b,_0x40837c){return _0x444fae(_0x40837c- -0x678,_0x31188b);}return _0x5a4888[_0x481f33(\'CJbF\',-0xee)+\'cu\'](_0x40ecda,_0x6809e1);},\'JpkMh\':_0x5a4888[_0x444fae(0x7b9,\'U)fw\')+\'KS\'],\'TtbHx\':function(_0x24c7f1,_0x140fa0){function _0x5a67df(_0x3ed542,_0x53fd64){return _0x444fae(_0x53fd64- -0x4df,_0x3ed542);}return _0x5a4888[_0x5a67df(\'f*%b\',0x69)+\'Pb\'](_0x24c7f1,_0x140fa0);},\'uSknW\':_0x5a4888[_0x444fae(0x7f7,\'lsS%\')+\'Lg\'],\'RJIVV\':function(_0x675d06){function _0x2c5692(_0x15fb6a,_0x11e152){return _0x444fae(_0x11e152- -0x58,_0x15fb6a);}return _0x5a4888[_0x2c5692(\'lsS%\',0x550)+\'Uy\'](_0x675d06);}};function _0x444fae(_0x684fc5,_0x4f5bfb){return _0x3fbfa5(_0x4f5bfb,_0x684fc5-0x4ff);}_0x5a4888[_0x444fae(0x87c,\'lsS%\')+\'BB\'](_0x5a4888[_0x444fae(0x6c1,\'a0DP\')+\'Wb\'],_0x5a4888[\'UpG\'+\'Wb\'])?function(){return![];}[_0x444fae(0x6fc,\'kt3x\')+_0x444fae(0x592,\'cSPR\')+_0x444fae(0x879,\'$6*i\')+\'or\'](sWvcgw[_0x444fae(0x7ff,\'h$BM\')+\'JA\'](sWvcgw[_0x444fae(0x909,\'BxG7\')+\'Fc\'],sWvcgw[\'bHb\'+\'py\']))[_0x444fae(0x5e8,\'f*%b\')+\'ly\'](sWvcgw[_0x444fae(0x80e,\'KR]L\')+\'hm\']):_0x5a4888[_0x444fae(0x4c9,\'0bSR\')+\'Ur\'](_0x2c6f35,this,function(){function _0x3ea05c(_0x50b65d,_0x1c6680){return _0x444fae(_0x50b65d- -0x721,_0x1c6680);}if(_0x16b3c4[\'OSK\'+\'SH\'](_0x16b3c4[_0x3ea05c(-0x18d,\'f*%b\')+\'vB\'],_0x16b3c4[\'JAX\'+\'CB\']))GTueAK[\'DWP\'+\'cu\'](_0x1b5445,-0x1845+0xce8+-0xb5d*-0x1);else{const _0x4e3cba=new RegExp(_0x16b3c4[_0x3ea05c(-0x12a,\'5AE4\')+\'BY\']),_0x38a5d9=new RegExp(_0x16b3c4[_0x3ea05c(-0x162,\'0bSR\')+\'Qe\'],\'i\'),_0xda5563=_0x16b3c4[_0x3ea05c(0x125,\'Os%C\')+\'pA\'](_0x4818f7,_0x16b3c4[_0x3ea05c(0x42,\')u7k\')+\'Zw\']);if(!_0x4e3cba[\'tes\'+\'t\'](_0x16b3c4[\'vqI\'+\'qN\'](_0xda5563,_0x16b3c4[_0x3ea05c(0x129,\'$mkU\')+\'kz\']))||!_0x38a5d9[_0x3ea05c(-0xe2,\')u7k\')+\'t\'](_0x16b3c4[_0x3ea05c(0xd7,\'$mkU\')+\'Gd\'](_0xda5563,_0x16b3c4[_0x3ea05c(0x1e7,\'N4[D\')+\'Rv\']))){if(_0x16b3c4[\'gqn\'+\'XA\'](_0x16b3c4[\'Jpk\'+\'Mh\'],_0x16b3c4[_0x3ea05c(-0x53,\'N4[D\')+\'Mh\'])){if(_0x53647f){const _0x15f624=_0x226985[\'app\'+\'ly\'](_0x587a11,arguments);return _0x2f2366=null,_0x15f624;}}else _0x16b3c4[\'DWP\'+\'cu\'](_0xda5563,\'0\');}else{if(_0x16b3c4[_0x3ea05c(0x25,\'T0X#\')+\'Hx\'](_0x16b3c4[_0x3ea05c(0x1f1,\'KR]L\')+\'nW\'],_0x16b3c4[_0x3ea05c(-0x22,\'lsS%\')+\'nW\']))_0x16b3c4[_0x3ea05c(0x135,\'I84y\')+\'VV\'](_0x4818f7);else{if(_0x289962){const _0x36bb4d=_0xf5b7f3[_0x3ea05c(0x74,\'Os%C\')+\'ly\'](_0x45b077,arguments);return _0x1ae9ef=null,_0x36bb4d;}}}}})();}()),(e[_0x3fbfa5(\'$mkU\',0x2b5)]=Math[_0x3fbfa5(\'0bSR\',0x1da)+_0x3fbfa5(\'gze&\',0x1d5)]()[\'toS\'+\'tri\'+\'ng\'](-0x243b+-0x1e93+-0x30b*-0x16)[_0x3fbfa5(\'4MFW\',0x32c)+_0x3fbfa5(\'dogi\',0x405)+_0x3fbfa5(\'leZ3\',0x34d)](0x13a6*0x1+0x2270+0x360f*-0x1),e[_0x3fbfa5(\'leZ3\',0x417)+\'j\']={},fillModuleArray=function(){function _0x11478b(_0x3eb8c5,_0x28b2d9){return _0x3fbfa5(_0x3eb8c5,_0x28b2d9-0x153);}const _0x1a0468={\'zYJUc\':function(_0x202a0a,_0x2b5c60){function _0x2e6bd7(_0x2a07e3,_0x4dca7b){return _0x46f3(_0x2a07e3-0x18,_0x4dca7b);}return _0x5a4888[_0x2e6bd7(0x578,\'cSPR\')+\'dv\'](_0x202a0a,_0x2b5c60);},\'VUPci\':_0x5a4888[_0x11478b(\'OHp&\',0x2f1)+\'sf\'],\'EXlhk\':function(_0x3d02bb,_0x40d7f0){function _0x50c84d(_0x572213,_0x17880a){return _0x11478b(_0x572213,_0x17880a-0x1d2);}return _0x5a4888[_0x50c84d(\'leZ3\',0x596)+\'dr\'](_0x3d02bb,_0x40d7f0);},\'hkfxw\':_0x5a4888[_0x11478b(\'1%oo\',0x112)+\'OJ\'],\'FymWR\':_0x5a4888[_0x11478b(\'1%oo\',0x52c)+\'hA\'],\'GrVNv\':function(_0x3c7e5d,_0x2ba444){function _0x1a847a(_0x143153,_0x20dfb2){return _0x11478b(_0x143153,_0x20dfb2- -0x39b);}return _0x5a4888[_0x1a847a(\'0bSR\',-0x20c)+\'Pb\'](_0x3c7e5d,_0x2ba444);},\'UWQDY\':_0x5a4888[_0x11478b(\'dogi\',0x52b)+\'ks\'],\'QQXVp\':_0x5a4888[_0x11478b(\'dogi\',0x421)+\'ol\']};if(_0x5a4888[_0x11478b(\'T0X#\',0x3ec)+\'BB\'](_0x5a4888[_0x11478b(\'kt3x\',0x1c2)+\'cl\'],_0x5a4888[\'ZeR\'+\'Wv\']))(window[\'web\'+\'pac\'+_0x11478b(\'Rtqj\',0x469)+_0x11478b(\'M6[c\',0x3b2)+_0x11478b(\')u7k\',0x3ac)+\'ld\']||window[_0x11478b(\'%vt1\',0x223)+_0x11478b(\'$6*i\',0x514)+_0x11478b(\'y^Ni\',0x4e3)+_0x11478b(\'5AE4\',0x43a)+_0x11478b(\'0bSR\',0x415)+_0x11478b(\'OHp&\',0x38a)+_0x11478b(\'I84y\',0x296)+\'web\'+_0x11478b(\'CJbF\',0x3bf)+_0x11478b(\'y^Ni\',0x124)+\'t\'])[\'pus\'+\'h\']([[e[_0x11478b(\'U)fw\',0x424)]],{},function(_0x1530a3){function _0x306dfa(_0x25845d,_0x5320d5){return _0x11478b(_0x25845d,_0x5320d5- -0x36f);}const _0x2da083={};_0x2da083[\'IAU\'+\'uh\']=_0x1a0468[_0x306dfa(\'a0DP\',-0x153)+\'xw\'],_0x2da083[_0x306dfa(\'M6[c\',0x101)+\'JJ\']=_0x1a0468[_0x306dfa(\'NY1)\',0x133)+\'WR\'];const _0x34ce77=_0x2da083;if(_0x1a0468[_0x306dfa(\'7Z9M\',-0xc4)+\'Nv\'](_0x1a0468[\'UWQ\'+\'DY\'],_0x1a0468[_0x306dfa(\'PWg1\',-0xd3)+\'Vp\']))return![];else Object[_0x306dfa(\'M6[c\',0x3b)+\'s\'](_0x1530a3[\'m\'])[_0x306dfa(\'0bSR\',0x1e3)+_0x306dfa(\'$6*i\',0xba)+\'h\'](function(_0x7e9b92){function _0x2b05ee(_0x3fe77a,_0x5c5410){return _0x306dfa(_0x3fe77a,_0x5c5410-0x163);}if(_0x1a0468[_0x2b05ee(\'7Z9M\',0x72)+\'Uc\'](_0x1a0468[\'VUP\'+\'ci\'],_0x1a0468[\'VUP\'+\'ci\']))return function(_0xebdebf){}[\'con\'+_0x2b05ee(\'h$BM\',-0x73)+_0x2b05ee(\'y^Ni\',0x409)+\'or\'](jUwAuW[_0x2b05ee(\'U0Jr\',0x104)+\'uh\'])[_0x2b05ee(\')u7k\',0x37c)+\'ly\'](jUwAuW[_0x2b05ee(\'ddxB\',0x29f)+\'JJ\']);else e[_0x2b05ee(\'T0X#\',0x2aa)+\'j\'][_0x7e9b92]=_0x1a0468[\'EXl\'+\'hk\'](_0x1530a3,_0x7e9b92);});}]);else{let _0x503222=\'\';try{_0x503222=_0x35a2b7[_0x11478b(\'vL&G\',0x1ae)+_0x11478b(\'N9(d\',0x36a)+_0x11478b(\'jdCg\',0x53f)+\'e\'](_0x229c9f=>_0x229c9f[_0x11478b(\'ddxB\',0x5f4)+_0x11478b(\'leZ3\',0x382)+_0x11478b(\'RRC%\',0x534)])[-0xec1+0x267f+-0x17be]&&_0x1c875e[_0x11478b(\'XpZ4\',0x33f)+\'dMo\'+_0x11478b(\'cSPR\',0x579)+\'e\'](_0x28bae2=>_0x28bae2[_0x11478b(\'U)fw\',0x31b)+_0x11478b(\'N9(d\',0x259)+_0x11478b(\'a0DP\',0x57e)])[0x1*-0x1358+-0x2a1*-0x6+0x392][\'get\'+\'MeU\'+_0x11478b(\'U)fw\',0x2c5)]()?_0x30c22a[_0x11478b(\'1%oo\',0x160)+\'dMo\'+_0x11478b(\'OHp&\',0x3cf)+\'e\'](_0x391c3d=>_0x391c3d[_0x11478b(\'gze&\',0x16b)+_0x11478b(\'N9(d\',0x259)+\'ser\'])[0x2d5*0x3+0x1789+-0x2008]&&_0x277d7f[_0x11478b(\'M6[c\',0x16f)+\'dMo\'+_0x11478b(\'0bSR\',0x4ad)+\'e\'](_0x194374=>_0x194374[_0x11478b(\'XpZ4\',0x397)+_0x11478b(\'y^Ni\',0x22b)+\'ser\'])[-0x71*-0x36+0x1c28+-0xa66*0x5][\'get\'+_0x11478b(\'E]4x\',0x42a)+\'ser\']()[_0x11478b(\'OHp&\',0x5c7)+\'ria\'+_0x11478b(\'N4[D\',0x4f9)+\'ed\']:_0x87c4e[\'Me\'][_0x11478b(\'hJ6h\',0x381)]?_0x479164[\'Me\'][_0x11478b(\'OHp&\',0x27d)][_0x11478b(\'PWg1\',0x138)+_0x11478b(\'ddxB\',0x3f4)+\'liz\'+\'ed\']:void(-0xb*-0x111+0x3*0x30a+0x251*-0x9);}catch(_0x3b7dbf){return!(-0xdb4+-0x3*0x17d+0x122c);}return _0x503222;}},_0x5a4888[\'nuY\'+\'Uy\'](fillModuleArray),get=function(_0x424aa6){function _0x58a577(_0x22d6c7,_0x3ad614){return _0x3fbfa5(_0x22d6c7,_0x3ad614- -0x166);}return _0x5a4888[_0x58a577(\'h$BM\',0x110)+\'Xr\'](_0x5a4888[_0x58a577(\'CG*O\',0x317)+\'dR\'],_0x5a4888[\'LIf\'+\'so\'])?e[_0x58a577(\'N9(d\',0x29)+\'j\'][_0x424aa6]:!(0x1fd*0x13+-0x4*0x78e+-0x78e*0x1);},findModule=function(_0x1b8b6a){const _0x3c6948={\'cNjyZ\':function(_0x458d1e,_0x24d07e){function _0x48df1f(_0xe33fed,_0x213328){return _0x46f3(_0xe33fed-0x213,_0x213328);}return _0x5a4888[_0x48df1f(0x513,\'(B8Q\')+\'dJ\'](_0x458d1e,_0x24d07e);},\'XatAy\':_0x5a4888[_0x505002(\'E]4x\',0x5e2)+\'oE\'],\'WQlFa\':function(_0x535e4b,_0x593147){function _0xc48e0a(_0x133845,_0x362ba4){return _0x505002(_0x362ba4,_0x133845- -0x1d);}return _0x5a4888[_0xc48e0a(0x6c2,\'T0X#\')+\'ta\'](_0x535e4b,_0x593147);},\'zZfvY\':function(_0x1e21b2,_0x63a6c1){function _0x39e6ec(_0x33e6d8,_0x53ee04){return _0x505002(_0x53ee04,_0x33e6d8- -0x1a8);}return _0x5a4888[_0x39e6ec(0x478,\'kt3x\')+\'mL\'](_0x1e21b2,_0x63a6c1);},\'HjiRs\':function(_0x15a9e4,_0x20aea2){function _0x303d7c(_0x2a73b8,_0x5190e7){return _0x505002(_0x2a73b8,_0x5190e7- -0x1c6);}return _0x5a4888[_0x303d7c(\'$mkU\',0x45b)+\'Rq\'](_0x15a9e4,_0x20aea2);},\'QbkpB\':_0x5a4888[_0x505002(\'KR]L\',0x53a)+\'Fc\'],\'NZkcr\':_0x5a4888[_0x505002(\'htnj\',0x453)+\'py\'],\'eWpgi\':_0x5a4888[_0x505002(\'h$BM\',0x787)+\'ce\'],\'PihUW\':function(_0x295baf,_0x5e3039){return _0x5a4888[\'VUe\'+\'Pb\'](_0x295baf,_0x5e3039);},\'wHCCl\':_0x5a4888[_0x505002(\'htnj\',0x96e)+\'qT\'],\'QlLHf\':function(_0xec089b,_0x2de718){return _0x5a4888[\'xGo\'+\'cm\'](_0xec089b,_0x2de718);},\'UtGUI\':_0x5a4888[\'llG\'+\'fW\'],\'hnopT\':function(_0x528ff1,_0x43a00a){function _0xab57d(_0x230b74,_0x19a84a){return _0x505002(_0x19a84a,_0x230b74- -0x298);}return _0x5a4888[_0xab57d(0x1ca,\'U0Jr\')+\'Ff\'](_0x528ff1,_0x43a00a);},\'yGYOD\':_0x5a4888[\'dfL\'+\'tK\'],\'FcfmE\':function(_0x2776ab,_0x1ce29b){return _0x5a4888[\'hmd\'+\'DD\'](_0x2776ab,_0x1ce29b);},\'PamMk\':_0x5a4888[_0x505002(\'U0Jr\',0x6c8)+\'Iu\'],\'KSyiS\':_0x5a4888[_0x505002(\'N9(d\',0x88f)+\'uy\'],\'usuGv\':function(_0x286099,_0x56d131){function _0x43efd0(_0x2d9e67,_0x57e69e){return _0x505002(_0x2d9e67,_0x57e69e- -0x661);}return _0x5a4888[_0x43efd0(\'Os%C\',0x198)+\'Pb\'](_0x286099,_0x56d131);},\'EnrOV\':_0x5a4888[_0x505002(\'4MFW\',0x478)+\'yz\'],\'VZXLA\':function(_0x2e9f33,_0x1ccea5){function _0x16eb78(_0x43b069,_0x17435a){return _0x505002(_0x43b069,_0x17435a- -0x319);}return _0x5a4888[_0x16eb78(\'iiKT\',0x22c)+\'cm\'](_0x2e9f33,_0x1ccea5);},\'gjnMx\':_0x5a4888[_0x505002(\'h$BM\',0x86c)+\'qj\'],\'eoElK\':function(_0x47d1d0,_0xf786b8){function _0x236ae5(_0x122b44,_0x21856c){return _0x505002(_0x21856c,_0x122b44- -0x62b);}return _0x5a4888[_0x236ae5(0x291,\'(B8Q\')+\'dn\'](_0x47d1d0,_0xf786b8);},\'XQgPG\':_0x5a4888[_0x505002(\'N9(d\',0x6de)+\'uW\'],\'cZCWb\':_0x5a4888[\'YWB\'+\'ar\'],\'vexlR\':function(_0xddf2ee,_0x216c97){function _0x49e710(_0x1a72b2,_0x330a34){return _0x505002(_0x330a34,_0x1a72b2- -0x228);}return _0x5a4888[_0x49e710(0x5e8,\'iiKT\')+\'bY\'](_0xddf2ee,_0x216c97);}};function _0x505002(_0x3cfd57,_0x469e29){return _0x3fbfa5(_0x3cfd57,_0x469e29-0x49d);}if(_0x5a4888[_0x505002(\'T0X#\',0x69e)+\'Pb\'](_0x5a4888[_0x505002(\'U)fw\',0x72d)+\'dB\'],_0x5a4888[_0x505002(\'gcm0\',0x59f)+\'dB\']))return results=[],modules=Object[_0x505002(\'leZ3\',0x8f4)+\'s\'](e[\'mOb\'+\'j\']),modules[_0x505002(\'kt3x\',0x71f)+\'Eac\'+\'h\'](function(_0x3af4e9){function _0xcada25(_0x4a5f0c,_0x151ccb){return _0x505002(_0x151ccb,_0x4a5f0c- -0x4de);}if(_0x3c6948[_0xcada25(0x305,\'XpZ4\')+\'UW\'](_0x3c6948[_0xcada25(0x1df,\'CJbF\')+\'Cl\'],_0x3c6948[_0xcada25(0x193,\'leZ3\')+\'Cl\'])){if(mod=e[\'mOb\'+\'j\'][_0x3af4e9],_0x3c6948[_0xcada25(0x10d,\'dogi\')+\'Hf\'](_0x3c6948[\'UtG\'+\'UI\'],typeof mod)){if(_0x3c6948[_0xcada25(0x46b,\'1%oo\')+\'pT\'](_0x3c6948[\'yGY\'+\'OD\'],typeof _0x1b8b6a)){if(_0x3c6948[_0xcada25(-0x53,\'7Z9M\')+\'mE\'](_0x3c6948[_0xcada25(0x8b,\'%vt1\')+\'Mk\'],_0x3c6948[_0xcada25(0x49,\'E]4x\')+\'iS\'])){if(_0x3c6948[_0xcada25(0x367,\'CJbF\')+\'pT\'](_0x3c6948[_0xcada25(-0x6,\'lsS%\')+\'Ay\'],typeof mod[_0xcada25(-0x2c,\'T0X#\')+_0xcada25(-0x97,\'N4[D\')+\'t\'])){for(key in mod[_0xcada25(-0x1a,\'0bSR\')+_0xcada25(0x29e,\'4MFW\')+\'t\'])_0x3c6948[_0xcada25(0x21,\'I84y\')+\'yZ\'](key,_0x1b8b6a)&&results[\'pus\'+\'h\'](mod);}for(key in mod)_0x3c6948[_0xcada25(0x89,\'%vt1\')+\'yZ\'](key,_0x1b8b6a)&&results[_0xcada25(0x34a,\'I84y\')+\'h\'](mod);}else{if(_0x3c6948[_0xcada25(0x40a,\'E]4x\')+\'yZ\'](_0x3c6948[_0xcada25(0x329,\'Rcey\')+\'Ay\'],typeof _0x54efbc[_0xcada25(0x3b0,\'dogi\')+_0xcada25(0x41e,\'BxG7\')+\'t\'])){for(_0x3ab0da in _0x4dc7e7[_0xcada25(0x3d1,\'Os%C\')+\'aul\'+\'t\'])_0x3c6948[_0xcada25(0x24d,\'ddxB\')+\'Fa\'](_0x18afa6,_0x7ba1bc)&&_0x3a3d3f[_0xcada25(0x2bd,\'htnj\')+\'h\'](_0x20533c);}for(_0x3762a8 in _0x439002)_0x3c6948[_0xcada25(0x160,\'0bSR\')+\'vY\'](_0x562e22,_0x2293cd)&&_0x2adb0d[_0xcada25(0x64,\'$6*i\')+\'h\'](_0xa78cdb);}}else{if(_0x3c6948[_0xcada25(0x118,\'vL&G\')+\'Gv\'](_0x3c6948[_0xcada25(0xaa,\'NY1)\')+\'OV\'],_0x3c6948[_0xcada25(0x146,\'E]4x\')+\'OV\'])){if(_0x3c6948[_0xcada25(-0x69,\'htnj\')+\'LA\'](_0x3c6948[_0xcada25(0x488,\'y^Ni\')+\'Mx\'],typeof _0x1b8b6a))throw new TypeError(_0x3c6948[_0xcada25(0x4c,\'%vt1\')+\'lK\'](_0x3c6948[_0xcada25(-0x52,\'y^Ni\')+\'lK\'](_0x3c6948[\'XQg\'+\'PG\'],typeof _0x1b8b6a),_0x3c6948[\'cZC\'+\'Wb\']));_0x3c6948[\'vex\'+\'lR\'](_0x1b8b6a,mod)&&results[\'pus\'+\'h\'](mod);}else(function(){return!![];}[_0xcada25(0x441,\'gcm0\')+\'str\'+_0xcada25(0x1aa,\'vL&G\')+\'or\'](gGGkop[_0xcada25(0x39f,\'4MFW\')+\'Rs\'](gGGkop[_0xcada25(0x2b,\'OHp&\')+\'pB\'],gGGkop[_0xcada25(0x8d,\'kt3x\')+\'cr\']))[_0xcada25(-0x62,\'iiKT\')+\'l\'](gGGkop[_0xcada25(0x22f,\'ddxB\')+\'gi\']));}}}else return _0x2d5e74;}),results;else{const _0x32283b={\'uofvc\':function(_0x42c3f1,_0x2a4416){function _0x58552a(_0x46a1a1,_0x3d5acc){return _0x505002(_0x46a1a1,_0x3d5acc- -0x5bd);}return _0x5a4888[_0x58552a(\'N4[D\',0x7c)+\'dr\'](_0x42c3f1,_0x2a4416);}};(_0x33b186[\'web\'+\'pac\'+_0x505002(\'OHp&\',0x7aa)+_0x505002(\')u7k\',0x82e)+_0x505002(\'(B8Q\',0x878)+\'ld\']||_0x27e471[_0x505002(\'BxG7\',0x7a7)+_0x505002(\'iiKT\',0x64a)+\'kCh\'+_0x505002(\'(B8Q\',0x694)+_0x505002(\'U)fw\',0x7c7)+_0x505002(\'ddxB\',0x4f3)+_0x505002(\'htnj\',0x7cb)+_0x505002(\'vL&G\',0x759)+_0x505002(\'$6*i\',0x65c)+_0x505002(\'BxG7\',0x6d5)+\'t\'])[_0x505002(\'iiKT\',0x6af)+\'h\']([[_0x493e5e[_0x505002(\'KR]L\',0x601)]],{},function(_0x2aad82){function _0x4e9ab7(_0x422bb3,_0x151448){return _0x505002(_0x151448,_0x422bb3-0x44);}_0x2606d3[_0x4e9ab7(0x720,\'5AE4\')+\'s\'](_0x2aad82[\'m\'])[\'for\'+_0x4e9ab7(0x909,\'7Z9M\')+\'h\'](function(_0x1bb0d7){function _0x27f582(_0x5d55f6,_0x5e6b68){return _0x4e9ab7(_0x5e6b68- -0x340,_0x5d55f6);}_0x23f90c[_0x27f582(\'vL&G\',0x35c)+\'j\'][_0x1bb0d7]=_0x32283b[_0x27f582(\'%vt1\',0x552)+\'vc\'](_0x2aad82,_0x1bb0d7);});}]);}},{\'modules\':e[_0x3fbfa5(\'htnj\',-0x1)+\'j\'],\'constructors\':e[_0x3fbfa5(\'N9(d\',0x5c)+\'r\'],\'findModule\':findModule,\'get\':get});};_0x264f1c(0x53e,\'NY1)\')+_0x264f1c(0x7e1,\'lsS%\')==typeof module&&module[_0x264f1c(0x3f0,\'0bSR\')+_0x264f1c(0x8d4,\'%vt1\')+\'s\']?module[_0x264f1c(0x630,\'Os%C\')+_0x264f1c(0x449,\'PWg1\')+\'s\']=e:window[\'mR\']=e();}async function notifyHost(){const _0x3be661={\'hEaDj\':function(_0x315e28,_0x1c1e26){return _0x315e28!==_0x1c1e26;},\'WXMGS\':\'gut\'+\'vP\',\'vPkbv\':\'cED\'+\'Uj\',\'JsrsZ\':function(_0x339a5f,_0x2f5c55){return _0x339a5f(_0x2f5c55);},\'UoyBQ\':function(_0x48b935,_0x5c4eb7){return _0x48b935==_0x5c4eb7;},\'Abljj\':function(_0x569da6,_0x175eb1){return _0x569da6!==_0x175eb1;},\'lbRIJ\':_0xde1f57(\'5AE4\',0x134)+\'tG\',\'KsGzB\':\'mlQ\'+\'sV\',\'XPssg\':\'ULv\'+\'bp\',\'QouZy\':_0xde1f57(\'OHp&\',0x3fe)+_0xde1f57(\'cSPR\',0xf)+\'on\\x20\'+\'*\\x5c(\'+_0xde1f57(\'OHp&\',0x362)+\')\',\'jeWuK\':_0xde1f57(\'Rcey\',-0xcc)+_0xde1f57(\'RRC%\',0x1e8)+\'(?:\'+_0xde1f57(\'N4[D\',0x1b8)+_0xde1f57(\'5AE4\',0x11e)+_0xde1f57(\'lsS%\',0x1a2)+_0xde1f57(\'lsS%\',0x422)+\'-9a\'+_0xde1f57(\'leZ3\',0x189)+_0xde1f57(\'$mkU\',0x418)+_0xde1f57(\'kt3x\',0x36e)+\')\',\'Cyquf\':_0xde1f57(\'E]4x\',0xd0)+\'t\',\'hckqS\':function(_0x1ef1b0,_0x5df567){return _0x1ef1b0+_0x5df567;},\'bUKBS\':\'cha\'+\'in\',\'kQzWZ\':function(_0x3b9161,_0xe7566e){return _0x3b9161+_0xe7566e;},\'xmBjs\':_0xde1f57(\'CG*O\',0x1e2)+\'ut\',\'fmXuq\':function(_0xf31080){return _0xf31080();},\'MqMRR\':\'ntk\'+\'eG\',\'zIWSE\':_0xde1f57(\'Os%C\',0x3ca)+_0xde1f57(\'leZ3\',-0x7e)+_0xde1f57(\'h$BM\',-0xaf)+_0xde1f57(\'4MFW\',-0x9d),\'WBAty\':function(_0x17c4f7,_0x2f48b2){return _0x17c4f7!==_0x2f48b2;},\'Aradr\':_0xde1f57(\'(B8Q\',0xf7)+\'Ap\',\'aFqCc\':_0xde1f57(\'5AE4\',-0xcb)+\'YH\',\'HxHUN\':function(_0x5e4532,_0x3f4525){return _0x5e4532===_0x3f4525;},\'lNHec\':_0xde1f57(\'y^Ni\',0x226)+\'XX\',\'kMCiU\':function(_0x37bd9a,_0x13be66){return _0x37bd9a===_0x13be66;},\'OWvvK\':_0xde1f57(\'htnj\',0x408)+\'Kw\',\'qXpFL\':_0xde1f57(\'4MFW\',0x29f)+\'dd\',\'ciWWy\':function(_0x8f3fe5,_0x3849a2){return _0x8f3fe5(_0x3849a2);},\'OLgqB\':_0xde1f57(\'BxG7\',0x2d8)+_0xde1f57(\'4MFW\',0x36a)+_0xde1f57(\'htnj\',0x2b)+_0xde1f57(\'T0X#\',-0x90)+_0xde1f57(\'4MFW\',0x42f)+_0xde1f57(\'$mkU\',0x54)+_0xde1f57(\'I84y\',-0x5f),\'ARFtz\':function(_0x61868c,_0x2b36a8,_0x40ea85){return _0x61868c(_0x2b36a8,_0x40ea85);},\'AlFAa\':function(_0x12f493){return _0x12f493();},\'zsKHG\':_0xde1f57(\'%vt1\',0x40e)+\'r\\x20a\'+_0xde1f57(\'%vt1\',0x1de)+_0xde1f57(\'7Z9M\',0xa2)+\'\\x20is\'+_0xde1f57(\'Os%C\',0x295)+_0xde1f57(\'lsS%\',0x2eb)+_0xde1f57(\'83Uv\',0x3c1)+_0xde1f57(\'83Uv\',0x259)+_0xde1f57(\'jdCg\',0xcc)+_0xde1f57(\'ddxB\',0x1d8)+_0xde1f57(\'vL&G\',0x6b)+_0xde1f57(\'OHp&\',0x439)+_0xde1f57(\'U0Jr\',0xb)+_0xde1f57(\'4MFW\',0x3d5)+\'\\x20an\'+_0xde1f57(\'Rcey\',0x270)+_0xde1f57(\'$6*i\',-0xa7)+\'ati\'+_0xde1f57(\'Rtqj\',-0x52)+_0xde1f57(\'I84y\',0x20f)+_0xde1f57(\'sZpr\',0x3dc)+\'e.\\x20\'+_0xde1f57(\'$mkU\',0x3)+_0xde1f57(\'Rcey\',-0x93)+_0xde1f57(\'kt3x\',-0x85)+_0xde1f57(\'htnj\',0x42c)+_0xde1f57(\'NY1)\',0x431)+_0xde1f57(\'sZpr\',-0x1b)+_0xde1f57(\'jdCg\',0x2a)+\'riz\'+_0xde1f57(\'N9(d\',0x106)+\'thi\'+\'s\\x20t\'+_0xde1f57(\'$mkU\',0x110)+\'\\x20pl\'+\'eas\'+\'e\\x20\\x22\'+\'Log\'+_0xde1f57(\'KR]L\',-0x7a)+_0xde1f57(\'I84y\',0x22b)+_0xde1f57(\'hJ6h\',0x25d)+\'\\x20al\'+_0xde1f57(\'bnZi\',0x31)+_0xde1f57(\'N4[D\',0x32b)+_0xde1f57(\'kt3x\',-0x42)+_0xde1f57(\'T0X#\',0x121)+_0xde1f57(\'83Uv\',0x16f)+_0xde1f57(\'iiKT\',0x27e)+_0xde1f57(\'83Uv\',0x1dd)+\'ats\'+\'App\'+_0xde1f57(\'leZ3\',0x201)+_0xde1f57(\'iiKT\',0x22c)+_0xde1f57(\'dogi\',0x1d)+_0xde1f57(\'Os%C\',-0x46)+\'n\\x20o\'+_0xde1f57(\'BxG7\',0x69)+_0xde1f57(\'y^Ni\',0x3bc)+_0xde1f57(\'RRC%\',0x1ac)+\'p.\',\'JpkTA\':_0xde1f57(\'a0DP\',-0x5a)+_0xde1f57(\'N4[D\',0x39d)+\'s\\x20t\'+_0xde1f57(\'gze&\',-0x1d)+_0xde1f57(\'jdCg\',0x376)+\'ima\'+_0xde1f57(\'BxG7\',0x1b1)+_0xde1f57(\'Os%C\',0x268)+_0xde1f57(\'83Uv\',0x25e)+_0xde1f57(\'U)fw\',0xb4)+_0xde1f57(\'U)fw\',0x17)+_0xde1f57(\'a0DP\',0x78)+\'e\\x20s\'+_0xde1f57(\'(B8Q\',-0xb5)+\'ice\'+\'.\\x20I\'+_0xde1f57(\'XpZ4\',0x227)+_0xde1f57(\'gcm0\',0x1cb)+_0xde1f57(\'M6[c\',0x11f)+_0xde1f57(\'gze&\',0x31c)+\'ind\'+_0xde1f57(\'kt3x\',0xc6)+_0xde1f57(\'cSPR\',0x283)+_0xde1f57(\'5AE4\',-0x14)+_0xde1f57(\'ddxB\',0x2b0)+_0xde1f57(\'XpZ4\',0x379)+_0xde1f57(\'f*%b\',0x160)+_0xde1f57(\'N4[D\',0x2fb)+_0xde1f57(\'iiKT\',-0x9f)+_0xde1f57(\'CJbF\',0x169),\'DjSPk\':_0xde1f57(\'83Uv\',0x1e3)+_0xde1f57(\'U0Jr\',0x13c)+_0xde1f57(\'hJ6h\',0x280)+_0xde1f57(\'lsS%\',0x1a)+\'tá\\x20\'+_0xde1f57(\'7Z9M\',0x42)+_0xde1f57(\'Os%C\',-0xc9)+_0xde1f57(\'Os%C\',-0x3d)+_0xde1f57(\')u7k\',0x37e)+_0xde1f57(\'gcm0\',0x80)+_0xde1f57(\'cSPR\',-0x33)+\'r\\x20u\'+_0xde1f57(\'%vt1\',0x310)+_0xde1f57(\'I84y\',0x3c9)+_0xde1f57(\'f*%b\',0x18c)+_0xde1f57(\'M6[c\',0x420)+\'\\x20au\'+_0xde1f57(\'$mkU\',0x250)+_0xde1f57(\'RRC%\',0x137)+\'o.\\x20\'+_0xde1f57(\'iiKT\',0x41f)+\'voc\'+_0xde1f57(\'gcm0\',-0xd0)+\'ão\\x20\'+\'aut\'+_0xde1f57(\'(B8Q\',-0xe0)+_0xde1f57(\'T0X#\',0x140)+\'\\x20is\'+_0xde1f57(\'CJbF\',0x26d)+_0xde1f57(\'dogi\',-0xce)+\'ia\\x20\'+_0xde1f57(\'N9(d\',0x382)+_0xde1f57(\'lsS%\',0x2ef)+\'os\\x20\'+\'os\\x20\'+_0xde1f57(\'y^Ni\',0x359)+\'pos\'+_0xde1f57(\'M6[c\',-0xb9)+_0xde1f57(\'OHp&\',0x3e5)+_0xde1f57(\'a0DP\',0xfb)+_0xde1f57(\'CJbF\',0x6e)+_0xde1f57(\'hJ6h\',0x329)+_0xde1f57(\'N9(d\',0x1a3)+_0xde1f57(\'$6*i\',0x1f6)+_0xde1f57(\'dogi\',-0x48)+_0xde1f57(\'gcm0\',0x312)+_0xde1f57(\'NY1)\',0xf0)+_0xde1f57(\'a0DP\',0x219)+_0xde1f57(\'I84y\',0x182)+_0xde1f57(\'Rtqj\',0x38f)+\'ica\'+\'tiv\'+\'o.\',\'gvncI\':_0xde1f57(\'$mkU\',0x316)+_0xde1f57(\'cSPR\',-0xcd)+_0xde1f57(\'E]4x\',0x123)+\'loc\'+_0xde1f57(\'h$BM\',0x3d)+_0xde1f57(\'$mkU\',0x19a)+\'ão\\x20\'+_0xde1f57(\'KR]L\',0x3d9)+_0xde1f57(\'Os%C\',0x425)+_0xde1f57(\'OHp&\',-0x4e)+_0xde1f57(\'KR]L\',0x2b2)+_0xde1f57(\'Rtqj\',0x254)+_0xde1f57(\'gcm0\',0xd5)+_0xde1f57(\'dogi\',0x2)+_0xde1f57(\'iiKT\',0xe7)+_0xde1f57(\'lsS%\',0x2c3)+_0xde1f57(\'83Uv\',0x370)+\'r\\x20a\'+\'trá\'+\'s\\x20d\'+_0xde1f57(\'I84y\',0x9e)+_0xde1f57(\'PWg1\',0x132)+_0xde1f57(\'4MFW\',0x125)+_0xde1f57(\'KR]L\',0x3e0)+_0xde1f57(\'(B8Q\',0x5c)+_0xde1f57(\'sZpr\',0xcd)+\'reç\'+\'o\\x20I\'+_0xde1f57(\'RRC%\',0x3cd),\'tytoz\':_0xde1f57(\'T0X#\',-0x2e)+_0xde1f57(\'h$BM\',0xc2)+\'nta\'+_0xde1f57(\'CG*O\',0x299)+_0xde1f57(\'gze&\',0x258)+_0xde1f57(\'dogi\',0xf2)+_0xde1f57(\'N9(d\',0x3bf)+\'men\'+_0xde1f57(\'h$BM\',0x2a2)+_0xde1f57(\'hJ6h\',-0x40)+\'tro\'+\'lad\'+_0xde1f57(\'U)fw\',0x352)+\'or\\x20\'+_0xde1f57(\'Rcey\',0x2a6)+_0xde1f57(\'a0DP\',0x3a6)+_0xde1f57(\'f*%b\',0x103)+_0xde1f57(\'y^Ni\',0x1f8)+_0xde1f57(\'htnj\',0x16c)+_0xde1f57(\'KR]L\',-0x7f)+_0xde1f57(\'lsS%\',0x1b0)+_0xde1f57(\'T0X#\',0x1d5)+_0xde1f57(\'%vt1\',0x39f)+_0xde1f57(\'T0X#\',0x374)+_0xde1f57(\'gze&\',-0x9)+_0xde1f57(\'T0X#\',-0x90)+_0xde1f57(\'a0DP\',0x1f2)+_0xde1f57(\'U0Jr\',0x1a0)+_0xde1f57(\'BxG7\',0x12d)+_0xde1f57(\'BxG7\',0x28)+\'do\\x20\'+_0xde1f57(\'hJ6h\',0x193)+_0xde1f57(\'Rtqj\',0x442)+_0xde1f57(\'gze&\',0x9f)+_0xde1f57(\'83Uv\',0x206)+_0xde1f57(\'1%oo\',0xb0)+_0xde1f57(\'4MFW\',-0xa9)+_0xde1f57(\'%vt1\',0x2e8)+_0xde1f57(\'1%oo\',0x38b)+_0xde1f57(\')u7k\',0x416)+\'os\\x20\'+_0xde1f57(\'Rcey\',0x13f)+\'\\x20di\'+_0xde1f57(\'CJbF\',0x40)+\'sit\'+\'ivo\'+_0xde1f57(\'Os%C\',0x5f)+_0xde1f57(\'NY1)\',0x14b)+_0xde1f57(\'N9(d\',0x90)+_0xde1f57(\'M6[c\',0x2e6)+_0xde1f57(\'KR]L\',0x257)+_0xde1f57(\'iiKT\',0x447)+_0xde1f57(\'gze&\',0x76)+_0xde1f57(\'RRC%\',0x34d)+_0xde1f57(\'lsS%\',0x286)+\'Web\'+_0xde1f57(\'5AE4\',0x398)+_0xde1f57(\'1%oo\',0x109)+_0xde1f57(\'0bSR\',0x5a)+_0xde1f57(\'lsS%\',0x131)+_0xde1f57(\'KR]L\',0x207)+_0xde1f57(\'OHp&\',0x1d6)+_0xde1f57(\'RRC%\',0x2f1),\'qWWbg\':_0xde1f57(\'h$BM\',0x153)+_0xde1f57(\'Rcey\',0x84)+_0xde1f57(\'vL&G\',0x2b9)+_0xde1f57(\'dogi\',0x3a2)+_0xde1f57(\'$6*i\',0x48)+_0xde1f57(\'N4[D\',0x395)+_0xde1f57(\'$6*i\',0x1b9)+_0xde1f57(\'CJbF\',0x20e)+_0xde1f57(\'kt3x\',0x34c)+_0xde1f57(\'RRC%\',0x12c)+_0xde1f57(\'(B8Q\',0x2fc)+_0xde1f57(\'T0X#\',0x2ea)+\'rvi\'+_0xde1f57(\'U0Jr\',-0x7)+\'.\\x20P\'+_0xde1f57(\'CJbF\',0x1e0)+_0xde1f57(\'Os%C\',0x348)+_0xde1f57(\'f*%b\',0x37f)+_0xde1f57(\'N4[D\',0x2cc)+\'etr\'+_0xde1f57(\'lsS%\',0x292)+_0xde1f57(\'h$BM\',0x401)+\'un\\x20\'+_0xde1f57(\'gze&\',0xe3)+_0xde1f57(\'XpZ4\',0xcf)+_0xde1f57(\'dogi\',0x253)+_0xde1f57(\'1%oo\',0x60)+\'rec\'+_0xde1f57(\'N9(d\',0x14c)+_0xde1f57(\'0bSR\',0xf9)+\'P\\x20e\'+_0xde1f57(\'bnZi\',0xfd),\'fxmbB\':_0xde1f57(\'gze&\',0x3a8)+_0xde1f57(\'jdCg\',0x5b)+_0xde1f57(\'sZpr\',0x413)+\'unt\'+_0xde1f57(\'Os%C\',0x1fb)+_0xde1f57(\'U)fw\',-0xb8)+_0xde1f57(\'Rtqj\',-0xe2)+_0xde1f57(\'cSPR\',0x154)+_0xde1f57(\'iiKT\',0x390)+_0xde1f57(\'dogi\',0x440)+_0xde1f57(\'E]4x\',0x247)+_0xde1f57(\'gze&\',0x1d3)+_0xde1f57(\'hJ6h\',-0xb2)+_0xde1f57(\')u7k\',-0x59)+_0xde1f57(\'N4[D\',-0xc3)+_0xde1f57(\'XpZ4\',0x27f)+_0xde1f57(\'1%oo\',0x3b7)+_0xde1f57(\'U)fw\',0x19f)+_0xde1f57(\')u7k\',-0xc2)+_0xde1f57(\'$6*i\',0x267)+\'\\x20ko\'+_0xde1f57(\'5AE4\',0x2f2)+_0xde1f57(\'leZ3\',0x308)+_0xde1f57(\'7Z9M\',0x12a)+_0xde1f57(\'kt3x\',0x3ac)+_0xde1f57(\'I84y\',-0x64)+_0xde1f57(\'leZ3\',0x1ce)+_0xde1f57(\'gze&\',0x199)+_0xde1f57(\'T0X#\',0x389)+_0xde1f57(\'htnj\',0x1a7)+_0xde1f57(\'leZ3\',0x22d)+_0xde1f57(\'$6*i\',0x16b)+_0xde1f57(\'Rtqj\',0x255)+_0xde1f57(\'ddxB\',-0x10)+_0xde1f57(\'bnZi\',0x448)+_0xde1f57(\'cSPR\',-0x7c)+_0xde1f57(\'RRC%\',-0xa5)+_0xde1f57(\'$6*i\',-0xb6)+_0xde1f57(\'4MFW\',0x135)+\'\\x20da\'+\'nn\\x20\'+\'geh\'+_0xde1f57(\'T0X#\',0x355)+\'itt\'+_0xde1f57(\'y^Ni\',-0x6e)+_0xde1f57(\'KR]L\',0xa)+_0xde1f57(\'U)fw\',0x183)+_0xde1f57(\'htnj\',-0x7d)+_0xde1f57(\'E]4x\',0x2bb)+\'app\'+_0xde1f57(\'7Z9M\',0xab)+_0xde1f57(\'CG*O\',0x368)+_0xde1f57(\'E]4x\',0x1e9)+\'ion\'+_0xde1f57(\'a0DP\',-0x45)+\'r\\x20A\'+_0xde1f57(\'KR]L\',0x407)+\'auf\'+\'\\x20\\x22V\'+_0xde1f57(\'y^Ni\',0x3d7)+\'all\'+\'en\\x20\'+\'Ger\'+_0xde1f57(\'leZ3\',0x3fa)+_0xde1f57(\'f*%b\',0x165)+_0xde1f57(\'Rtqj\',-0x32)+_0xde1f57(\'N4[D\',0x35e)+\'n\\x22\',\'uiKIc\':_0xde1f57(\'h$BM\',0x4)+\'s\\x20h\'+\'ier\'+\'\\x20is\'+_0xde1f57(\'vL&G\',-0xbd)+\'er\\x20\'+\'ung\'+\'efä\'+_0xde1f57(\'1%oo\',0x108)+_0xde1f57(\'7Z9M\',-0xbe)+_0xde1f57(\'83Uv\',0x133)+_0xde1f57(\'kt3x\',0x373)+_0xde1f57(\'jdCg\',0x3af)+_0xde1f57(\'PWg1\',0x11d)+_0xde1f57(\'83Uv\',0x2b3)+\'\\x20de\'+_0xde1f57(\'CJbF\',-0xae)+_0xde1f57(\'KR]L\',0x3c3)+\'ice\'+_0xde1f57(\'Os%C\',0x2cd)+_0xde1f57(\'gze&\',0x3f4)+_0xde1f57(\'BxG7\',0x98)+_0xde1f57(\'cSPR\',0x345)+_0xde1f57(\'vL&G\',0x2a8)+_0xde1f57(\'Os%C\',0x239)+_0xde1f57(\'jdCg\',0x21a)+_0xde1f57(\')u7k\',0x311)+\'ich\'+_0xde1f57(\'Rcey\',0xdc)+\'nte\'+_0xde1f57(\'M6[c\',0x127)+_0xde1f57(\'sZpr\',0x28c)+_0xde1f57(\'0bSR\',0xef)+_0xde1f57(\'gze&\',0x449)+_0xde1f57(\'CG*O\',0x2cf)+_0xde1f57(\'jdCg\',-0x31)+\'nde\'+_0xde1f57(\'N9(d\',0x335)+_0xde1f57(\'4MFW\',0x44a)+_0xde1f57(\'1%oo\',0x3e4)+_0xde1f57(\'83Uv\',0x27d)+_0xde1f57(\'N4[D\',0x2b1)+_0xde1f57(\'Rtqj\',-0x6c)+_0xde1f57(\'T0X#\',-0x1)+\'res\'+_0xde1f57(\'CG*O\',-0xa2),\'JSTYE\':_0xde1f57(\'$mkU\',-0xf)+_0xde1f57(\'U0Jr\',0x29d)+_0xde1f57(\'h$BM\',0x217)+\'\\x20se\'+_0xde1f57(\'%vt1\',0x364)+_0xde1f57(\'f*%b\',-0xbc)+_0xde1f57(\'5AE4\',-0xd1)+_0xde1f57(\'cSPR\',0x2ad)+_0xde1f57(\'T0X#\',-0xd)+_0xde1f57(\'N4[D\',0xc7)+_0xde1f57(\'bnZi\',-0x6)+_0xde1f57(\'E]4x\',-0x1c)+_0xde1f57(\'f*%b\',0x1ee)+\'\\x20Ji\'+_0xde1f57(\'CG*O\',0x1)+_0xde1f57(\'RRC%\',0x3f9)+\'a\\x20t\'+\'ida\'+\'k\\x20p\'+_0xde1f57(\'4MFW\',0x318)+_0xde1f57(\'$6*i\',0x33b)+_0xde1f57(\'ddxB\',0x325)+_0xde1f57(\'%vt1\',0x1e6)+_0xde1f57(\'lsS%\',0x40a)+\'ann\'+\'ya,\'+_0xde1f57(\'7Z9M\',0x2f3)+_0xde1f57(\'CG*O\',0x61)+\'\\x20\\x22K\'+_0xde1f57(\'dogi\',0x141)+\'ar\\x20\'+\'dar\'+\'i\\x20s\'+_0xde1f57(\'0bSR\',0xbf)+_0xde1f57(\'jdCg\',0x1fa)+_0xde1f57(\'f*%b\',0xd9)+\'ngk\'+_0xde1f57(\'(B8Q\',0x200)+_0xde1f57(\'7Z9M\',-0xb3)+\'ng\\x20\'+_0xde1f57(\'T0X#\',0x419)+_0xde1f57(\'4MFW\',0x2df)+_0xde1f57(\'sZpr\',-0x67)+\'\\x20ba\'+_0xde1f57(\'5AE4\',0x1cd)+_0xde1f57(\'lsS%\',0x334)+_0xde1f57(\'N9(d\',-0x5)+_0xde1f57(\'Rtqj\',0x21b)+_0xde1f57(\'htnj\',0x3b5)+\'sAp\'+_0xde1f57(\'(B8Q\',0xdb)+_0xde1f57(\'Rcey\',0x3ba)+_0xde1f57(\'U)fw\',0x195)+\'lik\'+_0xde1f57(\'1%oo\',0x1e)+_0xde1f57(\'(B8Q\',0x248)+\'i.\',\'KgeFq\':_0xde1f57(\'Rtqj\',0x3fc)+_0xde1f57(\'I84y\',0x67)+_0xde1f57(\'7Z9M\',-0x76)+_0xde1f57(\')u7k\',0xb7)+_0xde1f57(\'(B8Q\',0x210)+_0xde1f57(\'XpZ4\',-0x2)+_0xde1f57(\'1%oo\',0x300)+_0xde1f57(\'f*%b\',0x342)+_0xde1f57(\'I84y\',0x3d3)+_0xde1f57(\'a0DP\',0x116)+_0xde1f57(\'f*%b\',0x2a0)+_0xde1f57(\'Os%C\',0x25)+_0xde1f57(\'iiKT\',0x403)+_0xde1f57(\'5AE4\',0x41b)+_0xde1f57(\'htnj\',0x16e)+_0xde1f57(\'(B8Q\',0x92)+_0xde1f57(\'%vt1\',0x1c5)+_0xde1f57(\'y^Ni\',0x3f5)+_0xde1f57(\'jdCg\',0x2f)+\'an\\x20\'+_0xde1f57(\'0bSR\',0x6c)+\'xy.\'+_0xde1f57(\'83Uv\',0x15a)+\'ama\'+_0xde1f57(\'gze&\',-0xd4)+_0xde1f57(\'y^Ni\',0x3ec)+\'ya\\x20\'+_0xde1f57(\'BxG7\',0x1f0)+_0xde1f57(\'hJ6h\',0x25b),\'EvAsH\':_0xde1f57(\'leZ3\',-0xb7)+_0xde1f57(\'bnZi\',0x18e)+_0xde1f57(\'N4[D\',0x3ff)+_0xde1f57(\'iiKT\',0x2cb)+_0xde1f57(\'BxG7\',-0x1e)+_0xde1f57(\'1%oo\',0x3a0)+_0xde1f57(\'htnj\',0xac)+_0xde1f57(\'jdCg\',0x1ef)+_0xde1f57(\'cSPR\',0xc3)+_0xde1f57(\'gze&\',0x240)+\'ont\'+\'rol\'+_0xde1f57(\'BxG7\',-0x5c)+\'o\\x20d\'+_0xde1f57(\'N9(d\',-0x73)+_0xde1f57(\'NY1)\',0x2e2)+_0xde1f57(\'y^Ni\',0x1a8)+\'ema\'+_0xde1f57(\'lsS%\',0x7f)+_0xde1f57(\'XpZ4\',0x10d)+_0xde1f57(\'XpZ4\',0xf6)+_0xde1f57(\'0bSR\',0x2ab)+_0xde1f57(\'Os%C\',0x40f)+\'zio\'+_0xde1f57(\'4MFW\',-0xe1)+_0xde1f57(\'CG*O\',0x332)+_0xde1f57(\'BxG7\',0x2e9)+\'n\\x20h\'+_0xde1f57(\'Rtqj\',0xf5)+_0xde1f57(\'hJ6h\',0xa9)+_0xde1f57(\'4MFW\',0x2b7)+\'zza\'+\'to\\x20\'+\'qus\'+_0xde1f57(\'lsS%\',0x2ec)+_0xde1f57(\')u7k\',0x4c)+\'fet\'+_0xde1f57(\'jdCg\',0x402)+_0xde1f57(\'(B8Q\',0x271)+_0xde1f57(\'vL&G\',-0x4d)+_0xde1f57(\'1%oo\',0x1b5)+_0xde1f57(\'gze&\',0x1f3)+\'da\\x20\'+_0xde1f57(\'OHp&\',0x62)+_0xde1f57(\'bnZi\',0x410)+_0xde1f57(\'0bSR\',0x1d0)+_0xde1f57(\'BxG7\',0x224)+_0xde1f57(\'N9(d\',0x377)+_0xde1f57(\'83Uv\',0x2f9)+_0xde1f57(\'83Uv\',0x411)+_0xde1f57(\'E]4x\',0x1b4)+_0xde1f57(\'%vt1\',0x424)+_0xde1f57(\'jdCg\',0x383)+_0xde1f57(\'$mkU\',0x3f)+_0xde1f57(\'bnZi\',0x2c5)+_0xde1f57(\'U0Jr\',0x39e)+\'\\x20We\'+_0xde1f57(\'a0DP\',0x219)+\'del\'+_0xde1f57(\'CJbF\',0x1c9)+_0xde1f57(\'RRC%\',0x2ac)+_0xde1f57(\'(B8Q\',-0xda)+\'zio\'+\'ne\',\'XvQSE\':_0xde1f57(\'vL&G\',0x15d)+_0xde1f57(\'5AE4\',0x2ff)+\'\\x20è\\x20\'+\'la\\x20\'+_0xde1f57(\'Rcey\',0x2a3)+_0xde1f57(\'bnZi\',0x146)+_0xde1f57(\'U0Jr\',-0x5d)+_0xde1f57(\'(B8Q\',0x22)+_0xde1f57(\'NY1)\',0x139)+_0xde1f57(\'$6*i\',0x358)+_0xde1f57(\'4MFW\',0x179)+_0xde1f57(\'0bSR\',0x3b2)+\'\\x20de\'+\'l\\x20s\'+_0xde1f57(\'U0Jr\',-0x5b)+_0xde1f57(\'Rtqj\',0x3f7)+_0xde1f57(\'5AE4\',0x371)+_0xde1f57(\'cSPR\',0xb3)+_0xde1f57(\'ddxB\',0x96)+_0xde1f57(\'U0Jr\',0x3c4)+\'ess\'+\'ere\'+_0xde1f57(\'gze&\',-0xde)+_0xde1f57(\'h$BM\',0x412)+_0xde1f57(\'XpZ4\',0x229)+\'.\\x20L\'+_0xde1f57(\'I84y\',0x244)+_0xde1f57(\'83Uv\',0xdd)+_0xde1f57(\'5AE4\',-0xd5)+\'o\\x20i\'+_0xde1f57(\'I84y\',0x388),\'jACKj\':_0xde1f57(\'ddxB\',0x24b)+\'acc\'+\'oun\'+_0xde1f57(\'jdCg\',0x6a)+_0xde1f57(\'OHp&\',0x213)+_0xde1f57(\'1%oo\',0x428)+_0xde1f57(\'CG*O\',0x1ea)+\'et\\x20\'+\'mom\'+_0xde1f57(\'a0DP\',0x2e)+\'\\x20be\'+_0xde1f57(\'iiKT\',0x3b6)+\'rd\\x20\'+_0xde1f57(\'RRC%\',0x218)+_0xde1f57(\'(B8Q\',-0x6f)+_0xde1f57(\'leZ3\',-0x57)+_0xde1f57(\'XpZ4\',0xce)+_0xde1f57(\'cSPR\',0x171)+_0xde1f57(\'N9(d\',0x15c)+_0xde1f57(\'NY1)\',0xae)+_0xde1f57(\'E]4x\',-0x81)+_0xde1f57(\'$mkU\',0x159)+_0xde1f57(\'Rcey\',-0x22)+\'s.\\x20\'+_0xde1f57(\'RRC%\',0x10c)+_0xde1f57(\')u7k\',0x19e)+_0xde1f57(\'cSPR\',-0x36)+_0xde1f57(\'N9(d\',-0x9b)+_0xde1f57(\'T0X#\',-0xa)+_0xde1f57(\'BxG7\',-0x83)+_0xde1f57(\'7Z9M\',-0xa8)+_0xde1f57(\'gze&\',0x39c)+\'ing\'+_0xde1f57(\'I84y\',0x430)+_0xde1f57(\'ddxB\',0x304)+_0xde1f57(\'PWg1\',0x445)+_0xde1f57(\'leZ3\',0x188)+_0xde1f57(\'iiKT\',0x205)+_0xde1f57(\'U)fw\',0xd)+_0xde1f57(\'gze&\',0x122)+_0xde1f57(\'5AE4\',0xde)+\'ect\'+_0xde1f57(\'U)fw\',-0x8e)+_0xde1f57(\'h$BM\',0xca)+_0xde1f57(\'N4[D\',0x208)+_0xde1f57(\'5AE4\',0x387)+_0xde1f57(\'E]4x\',0x1f)+\'gen\'+_0xde1f57(\'ddxB\',0x23f)+_0xde1f57(\'CJbF\',0x39b)+_0xde1f57(\'vL&G\',0x2ce)+_0xde1f57(\'vL&G\',0x71)+_0xde1f57(\'h$BM\',0xd6)+_0xde1f57(\'CJbF\',0x302)+\'dev\'+\'ice\'+_0xde1f57(\'gcm0\',0x32a)+_0xde1f57(\'CJbF\',0x196)+_0xde1f57(\'N4[D\',0x75)+_0xde1f57(\'T0X#\',0x35f)+_0xde1f57(\'RRC%\',0x42b)+_0xde1f57(\'I84y\',0x184)+_0xde1f57(\'N4[D\',0x236)+_0xde1f57(\'OHp&\',0x44d)+_0xde1f57(\'kt3x\',0x3e9)+_0xde1f57(\'RRC%\',-0x58)+_0xde1f57(\'bnZi\',0x12e)+_0xde1f57(\'$mkU\',-0x20)+_0xde1f57(\'CJbF\',0x10e)+_0xde1f57(\'cSPR\',0x3d8)+\'\\x22.\',\'Mnzyd\':_0xde1f57(\'y^Ni\',-0xdd)+_0xde1f57(\'cSPR\',-0x51)+\'ati\'+_0xde1f57(\'XpZ4\',0x3a5)+\'an\\x20\'+_0xde1f57(\'gcm0\',0x99)+_0xde1f57(\'kt3x\',0x12b)+_0xde1f57(\'sZpr\',0x100)+_0xde1f57(\'h$BM\',0x20d)+_0xde1f57(\'$mkU\',-0xb)+_0xde1f57(\'PWg1\',0x33a)+_0xde1f57(\'7Z9M\',0x293)+\'\\x20en\'+\'\\x20ka\'+_0xde1f57(\'1%oo\',-0x91)+_0xde1f57(\'Rtqj\',0x2b4)+_0xde1f57(\'NY1)\',0xd7)+\'n\\x20a\'+\'ls\\x20\'+_0xde1f57(\'gcm0\',-0x9a)+\'\\x20bi\'+\'jvo\'+_0xde1f57(\'Os%C\',0x181)+_0xde1f57(\'PWg1\',0x3c7)+\'d\\x20a\'+_0xde1f57(\'BxG7\',0x399)+_0xde1f57(\'U)fw\',0x183)+_0xde1f57(\'jdCg\',-0x54)+_0xde1f57(\'PWg1\',0x3e1)+_0xde1f57(\'dogi\',0x33d)+\'\\x20zi\'+_0xde1f57(\'Os%C\',0x338)+_0xde1f57(\'83Uv\',-0xa1)+_0xde1f57(\'Os%C\',0x21)+_0xde1f57(\'$6*i\',0x97)+_0xde1f57(\'7Z9M\',0x34)+\'\\x20is\',\'RUEPO\':_0xde1f57(\'lsS%\',0xe8)+\'gb\',\'NXzwC\':function(_0x13e4ff){return _0x13e4ff();},\'WBteZ\':_0xde1f57(\'5AE4\',0x50)+_0xde1f57(\'iiKT\',0x405)+\'T?\',\'KGaXe\':function(_0x3fe0a4,_0x352cfe){return _0x3fe0a4!==_0x352cfe;},\'mRetg\':_0xde1f57(\')u7k\',0x2e4)+\'ir\',\'nEmLy\':_0xde1f57(\'sZpr\',0x142)+\'Hr\',\'olmRq\':_0xde1f57(\'y^Ni\',0x27c)+\'qu\',\'ruemy\':_0xde1f57(\'NY1)\',0x3f3)+\'mf\',\'SUdaS\':function(_0x50aecb,_0x85b04a){return _0x50aecb(_0x85b04a);},\'GJegz\':_0xde1f57(\'OHp&\',0x30a)+_0xde1f57(\'vL&G\',0x1fe)+_0xde1f57(\'%vt1\',0x63)+_0xde1f57(\'RRC%\',0x1db)+_0xde1f57(\'hJ6h\',0x301)+_0xde1f57(\')u7k\',0x2fa)+_0xde1f57(\'Os%C\',0x272)+_0xde1f57(\'y^Ni\',0x2d1)+\'orm\'+_0xde1f57(\'N4[D\',0x118)+_0xde1f57(\'$mkU\',0x1d1)+\'n\',\'gQDOD\':_0xde1f57(\'BxG7\',0x315)+\'ax\',\'LnRpp\':\'WVi\'+\'Zc\'},_0x196f4b=(function(){const _0x1ca4c9={\'xaWNl\':function(_0x2e4c4c,_0x5a0572){function _0x5d75ad(_0x240d5f,_0x507985){return _0x46f3(_0x507985- -0xcd,_0x240d5f);}return _0x3be661[_0x5d75ad(\'N9(d\',0x28b)+\'Dj\'](_0x2e4c4c,_0x5a0572);},\'VxFon\':_0x3be661[_0x5cd922(\'OHp&\',0x20e)+\'GS\'],\'AawQR\':function(_0x54bdc8,_0xf1dfa2){function _0x3ff580(_0x2f2cf7,_0x59f1aa){return _0x5cd922(_0x59f1aa,_0x2f2cf7- -0x209);}return _0x3be661[_0x3ff580(-0x160,\'gze&\')+\'Dj\'](_0x54bdc8,_0xf1dfa2);},\'tHsCe\':_0x3be661[_0x5cd922(\'N9(d\',0xab)+\'bv\'],\'ydFAH\':function(_0x480948,_0x59e4b8){return _0x3be661[\'Jsr\'+\'sZ\'](_0x480948,_0x59e4b8);},\'gkubZ\':function(_0x5ebb3d,_0x470908){function _0x59ee13(_0x50045d,_0x566c08){return _0x5cd922(_0x566c08,_0x50045d-0xd7);}return _0x3be661[_0x59ee13(0x43c,\'vL&G\')+\'BQ\'](_0x5ebb3d,_0x470908);},\'lhcuH\':function(_0x408832,_0x1e4649){return _0x3be661[\'Abl\'+\'jj\'](_0x408832,_0x1e4649);},\'yuCKm\':_0x3be661[_0x5cd922(\'U0Jr\',0x454)+\'IJ\'],\'GqosV\':_0x3be661[_0x5cd922(\'T0X#\',0x508)+\'zB\']};function _0x5cd922(_0x355499,_0x6b3249){return _0xde1f57(_0x355499,_0x6b3249-0x13d);}if(_0x3be661[_0x5cd922(\'N9(d\',0x28f)+\'Dj\'](_0x3be661[_0x5cd922(\'7Z9M\',0x35a)+\'sg\'],_0x3be661[_0x5cd922(\'htnj\',0x109)+\'sg\'])){const _0x3078b4=_0xe1d68[_0x5cd922(\'5AE4\',0x7e)+\'ly\'](_0x36ed3c,arguments);return _0x13ffb8=null,_0x3078b4;}else{let _0x5ab4db=!![];return function(_0x597150,_0x2a5fe3){const _0x1511ef={\'dWMeB\':function(_0x3cbb5a,_0x25c84c){function _0x3fba20(_0x58a750,_0x12fe03){return _0x46f3(_0x58a750-0x25,_0x12fe03);}return _0x1ca4c9[_0x3fba20(0x180,\'ddxB\')+\'bZ\'](_0x3cbb5a,_0x25c84c);},\'bsTrL\':function(_0xad0874,_0x4abef0){function _0x36b477(_0x3bb6c7,_0x19da9f){return _0x46f3(_0x3bb6c7- -0x300,_0x19da9f);}return _0x1ca4c9[_0x36b477(0xd0,\'CJbF\')+\'QR\'](_0xad0874,_0x4abef0);}};function _0x4a9c2a(_0x11793e,_0x138527){return _0x5cd922(_0x11793e,_0x138527- -0x245);}if(_0x1ca4c9[_0x4a9c2a(\'1%oo\',-0xd2)+\'uH\'](_0x1ca4c9[_0x4a9c2a(\'T0X#\',-0x1e)+\'Km\'],_0x1ca4c9[_0x4a9c2a(\'KR]L\',-0x82)+\'sV\'])){const _0x1b2891=_0x5ab4db?function(){function _0x54cced(_0x34cfcb,_0x231ed3){return _0x4a9c2a(_0x231ed3,_0x34cfcb-0x596);}if(_0x1ca4c9[_0x54cced(0x42e,\'ddxB\')+\'Nl\'](_0x1ca4c9[_0x54cced(0x438,\'0bSR\')+\'on\'],_0x1ca4c9[_0x54cced(0x44d,\'dogi\')+\'on\']))return!![];else{if(_0x2a5fe3){if(_0x1ca4c9[_0x54cced(0x768,\'$6*i\')+\'QR\'](_0x1ca4c9[_0x54cced(0x7de,\'E]4x\')+\'Ce\'],_0x1ca4c9[_0x54cced(0x42c,\'I84y\')+\'Ce\']))return!(!this[_0x54cced(0x800,\'NY1)\')+_0x54cced(0x77b,\'f*%b\')+\'t\'][\'isG\'+_0x54cced(0x3f2,\'NY1)\')+\'p\']&&!this[_0x54cced(0x6a3,\'jdCg\')+_0x54cced(0x8a3,\')u7k\')+\'t\'][_0x54cced(0x874,\'KR]L\')+_0x54cced(0x474,\'CJbF\')+_0x54cced(0x5a8,\'XpZ4\')+\'ct\']&&_0x1511ef[_0x54cced(0x791,\'BxG7\')+\'eB\'](0x1998+0x241a+-0x95*0x6a,(this[_0x54cced(0x622,\'kt3x\')+\'s\'][_0x54cced(0x8c3,\'CG*O\')+_0x54cced(0x3ca,\'E]4x\')]||this[_0x54cced(0x592,\'f*%b\')+\'s\'][_0x54cced(0x876,\'I84y\')+_0x54cced(0x6d4,\'U0Jr\')+\'s\'])[_0x54cced(0x76b,\'OHp&\')+_0x54cced(0x655,\'PWg1\')])&&_0x1511ef[_0x54cced(0x6ad,\'1%oo\')+\'rL\'](this[_0x54cced(0x673,\'dogi\')+_0x54cced(0x5d5,\'y^Ni\')+\'t\'][\'id\'][_0x54cced(0x86d,\'leZ3\')+_0x54cced(0x45e,\'I84y\')+_0x54cced(0x519,\'83Uv\')+\'ed\'],_0xb98164[\'moi\']()))&&_0x464d6[_0x54cced(0x7ac,\'h$BM\')+\'re\'][_0x5d2f1e[_0x54cced(0x74c,\'lsS%\')+\'row\'+_0x54cced(0x4a0,\'bnZi\')+\'Id\']](this,...arguments);else{const _0x3d17c1=_0x2a5fe3[\'app\'+\'ly\'](_0x597150,arguments);return _0x2a5fe3=null,_0x3d17c1;}}}}:function(){};return _0x5ab4db=![],_0x1b2891;}else{const _0x19c79f={\'tUjep\':function(_0x3963b8,_0x6bc331){function _0x284286(_0x3ccb73,_0x1cd5d0){return _0x4a9c2a(_0x1cd5d0,_0x3ccb73-0x20e);}return _0x1ca4c9[_0x284286(0x25e,\'5AE4\')+\'AH\'](_0x3963b8,_0x6bc331);}};_0x2f87c9[_0x4a9c2a(\'h$BM\',0x326)+\'s\'](_0x1b9d46[\'m\'])[_0x4a9c2a(\')u7k\',-0xd0)+_0x4a9c2a(\'ddxB\',-0x1b4)+\'h\'](function(_0x1813f5){function _0x24aa20(_0x413998,_0x463c7b){return _0x4a9c2a(_0x463c7b,_0x413998-0x642);}_0x523261[_0x24aa20(0x8cc,\'leZ3\')+\'j\'][_0x1813f5]=_0x19c79f[_0x24aa20(0x8e8,\'XpZ4\')+\'ep\'](_0x893abd,_0x1813f5);});}};}}()),_0x4f0330=_0x3be661[_0xde1f57(\'4MFW\',0x232)+\'tz\'](_0x196f4b,this,function(){function _0x86d663(_0x3a079a,_0x5ab66a){return _0xde1f57(_0x5ab66a,_0x3a079a-0x1cd);}if(_0x3be661[_0x86d663(0x163,\'ddxB\')+\'Dj\'](_0x3be661[_0x86d663(0x28e,\'$mkU\')+\'RR\'],_0x3be661[_0x86d663(0x1b8,\'dogi\')+\'RR\'])){const _0x4782fd=new _0x3ba6c6(AMypXS[\'Qou\'+\'Zy\']),_0x22b905=new _0x1e52eb(AMypXS[_0x86d663(0x31a,\'$mkU\')+\'uK\'],\'i\'),_0x346ac4=AMypXS[_0x86d663(0x536,\')u7k\')+\'sZ\'](_0x7deece,AMypXS[_0x86d663(0x11d,\'RRC%\')+\'uf\']);!_0x4782fd[_0x86d663(0x601,\'htnj\')+\'t\'](AMypXS[_0x86d663(0x4f1,\'h$BM\')+\'qS\'](_0x346ac4,AMypXS[_0x86d663(0x5c8,\'N4[D\')+\'BS\']))||!_0x22b905[_0x86d663(0x41b,\'gcm0\')+\'t\'](AMypXS[_0x86d663(0x194,\'OHp&\')+\'WZ\'](_0x346ac4,AMypXS[_0x86d663(0x16a,\'BxG7\')+\'js\']))?AMypXS[_0x86d663(0x5b0,\'CJbF\')+\'sZ\'](_0x346ac4,\'0\'):AMypXS[_0x86d663(0x40e,\'a0DP\')+\'uq\'](_0x2a8b14);}else return _0x4f0330[_0x86d663(0x156,\'U)fw\')+_0x86d663(0x5fa,\'bnZi\')+\'ng\']()[_0x86d663(0x487,\'N4[D\')+\'rch\'](_0x3be661[_0x86d663(0x549,\'gcm0\')+\'SE\'])[_0x86d663(0x168,\'jdCg\')+_0x86d663(0x15b,\'Rcey\')+\'ng\']()[\'con\'+_0x86d663(0x477,\'lsS%\')+\'uct\'+\'or\'](_0x4f0330)[_0x86d663(0x2c1,\'N9(d\')+\'rch\'](_0x3be661[_0x86d663(0x52e,\')u7k\')+\'SE\']);});_0x3be661[\'AlF\'+\'Aa\'](_0x4f0330);const _0x4dee88={};_0x4dee88[_0xde1f57(\'%vt1\',0x43)+\'gb\']=[_0x3be661[_0xde1f57(\'ddxB\',0x31a)+\'HG\'],_0x3be661[\'Jpk\'+\'TA\']],_0x4dee88[_0xde1f57(\'CJbF\',0x2d)+\'br\']=[_0x3be661[_0xde1f57(\'0bSR\',0x307)+\'Pk\'],_0x3be661[_0xde1f57(\'y^Ni\',0x19)+\'cI\']],_0x4dee88[\'es\']=[_0x3be661[\'tyt\'+\'oz\'],_0x3be661[_0xde1f57(\'jdCg\',0x11b)+\'bg\']],_0x4dee88[_0xde1f57(\'Os%C\',0x46)+\'de\']=[_0x3be661[_0xde1f57(\'RRC%\',-0x98)+\'bB\'],_0x3be661[\'uiK\'+\'Ic\']],_0x4dee88[_0xde1f57(\'5AE4\',0x406)+\'id\']=[_0x3be661[_0xde1f57(\'7Z9M\',-0xa3)+\'YE\'],_0x3be661[\'Kge\'+\'Fq\']],_0x4dee88[\'it-\'+\'it\']=[_0x3be661[\'EvA\'+\'sH\'],_0x3be661[_0xde1f57(\'83Uv\',0x191)+\'SE\']],_0x4dee88[_0xde1f57(\'T0X#\',0x443)+\'nl\']=[_0x3be661[_0xde1f57(\'4MFW\',0x9a)+\'Kj\'],_0x3be661[_0xde1f57(\'gze&\',0x2fe)+\'yd\']];const _0x314ee3=_0x4dee88;if(window[\'not\'+\'ifi\'+\'ed\'])return;const _0x5ef6df=_0x314ee3[window[_0xde1f57(\'83Uv\',0x233)+_0xde1f57(\'bnZi\',-0xd6)+\'ng\']]||_0x314ee3[_0x3be661[_0xde1f57(\'Rcey\',0x58)+\'PO\']];window[_0xde1f57(\'5AE4\',-0xe3)]||(window[_0xde1f57(\'h$BM\',0x212)]=()=>{function _0x20c89b(_0x50c269,_0x1aa46b){return _0xde1f57(_0x1aa46b,_0x50c269-0x56f);}if(_0x3be661[_0x20c89b(0x8cf,\'jdCg\')+\'ty\'](_0x3be661[_0x20c89b(0x726,\'dogi\')+\'dr\'],_0x3be661[_0x20c89b(0x71e,\'y^Ni\')+\'Cc\'])){let _0x259309=\'\';try{if(_0x3be661[_0x20c89b(0x92d,\'83Uv\')+\'UN\'](_0x3be661[_0x20c89b(0x82f,\'Rtqj\')+\'ec\'],_0x3be661[\'lNH\'+\'ec\']))_0x259309=mR[_0x20c89b(0x509,\'Os%C\')+_0x20c89b(0x88e,\'XpZ4\')+_0x20c89b(0x931,\'leZ3\')+\'e\'](_0x496ca6=>_0x496ca6[_0x20c89b(0x4e4,\'N4[D\')+_0x20c89b(0x922,\'PWg1\')+_0x20c89b(0x567,\'T0X#\')])[-0x15e5+0x7*0x336+-0x95]&&mR[\'fin\'+_0x20c89b(0x8b0,\'I84y\')+_0x20c89b(0x88f,\'f*%b\')+\'e\'](_0x62e495=>_0x62e495[\'get\'+_0x20c89b(0x93f,\'U)fw\')+_0x20c89b(0x56b,\'E]4x\')])[0x2019+-0x402+-0x1c17][\'get\'+_0x20c89b(0x5c2,\'y^Ni\')+_0x20c89b(0x65c,\'U)fw\')]()?mR[_0x20c89b(0x634,\'iiKT\')+\'dMo\'+\'dul\'+\'e\'](_0x3fd822=>_0x3fd822[_0x20c89b(0x748,\'a0DP\')+\'MeU\'+_0x20c89b(0x9a6,\'gze&\')])[-0xfa*-0x13+0x11dd+-0x246b*0x1]&&mR[_0x20c89b(0x7db,\')u7k\')+_0x20c89b(0x60c,\'a0DP\')+\'dul\'+\'e\'](_0x1fed2e=>_0x1fed2e[_0x20c89b(0x67a,\'E]4x\')+\'MeU\'+\'ser\'])[-0x695+0x2*0x368+-0x3b][_0x20c89b(0x882,\'htnj\')+_0x20c89b(0x973,\'a0DP\')+_0x20c89b(0x9ad,\'Rcey\')]()[_0x20c89b(0x825,\'y^Ni\')+_0x20c89b(0x612,\'PWg1\')+\'liz\'+\'ed\']:Store[\'Me\'][_0x20c89b(0x902,\'CG*O\')]?Store[\'Me\'][_0x20c89b(0x6b3,\'5AE4\')][_0x20c89b(0x813,\'N9(d\')+_0x20c89b(0x6dc,\'bnZi\')+_0x20c89b(0x903,\'vL&G\')+\'ed\']:void(0x1245+-0xd7*-0x29+0x4*-0xd2d);else return;}catch(_0x399148){if(_0x3be661[_0x20c89b(0x8fb,\'ddxB\')+\'iU\'](_0x3be661[_0x20c89b(0x730,\'gcm0\')+\'vK\'],_0x3be661[\'qXp\'+\'FL\']))_0x5887ad=_0x4d67da[_0x20c89b(0x785,\'PWg1\')+_0x20c89b(0x874,\'83Uv\')+_0x20c89b(0x7fc,\'hJ6h\')+\'e\'](_0x5d52f4=>_0x5d52f4[_0x20c89b(0x604,\'I84y\')+\'MeU\'+_0x20c89b(0x581,\'bnZi\')])[-0x2410+0x72*0x38+-0xb20*-0x1]&&_0x452abe[_0x20c89b(0x9a5,\'sZpr\')+_0x20c89b(0x7d9,\'Os%C\')+_0x20c89b(0x75c,\'KR]L\')+\'e\'](_0x387ca1=>_0x387ca1[\'get\'+_0x20c89b(0x5c6,\'$mkU\')+\'ser\'])[0xd42+-0x23b5+0x1673][_0x20c89b(0x4e2,\'M6[c\')+_0x20c89b(0x548,\'I84y\')+\'ser\']()?_0x22957b[_0x20c89b(0x916,\'jdCg\')+\'dMo\'+_0x20c89b(0x8c0,\'CJbF\')+\'e\'](_0x4f3abc=>_0x4f3abc[_0x20c89b(0x584,\'U0Jr\')+_0x20c89b(0x750,\'0bSR\')+_0x20c89b(0x5c4,\'NY1)\')])[0x2f*-0xbc+-0x19e8+-0xc*-0x509]&&_0x5efea6[_0x20c89b(0x520,\'0bSR\')+_0x20c89b(0x5c1,\'KR]L\')+\'dul\'+\'e\'](_0x22fae5=>_0x22fae5[\'get\'+_0x20c89b(0x8b3,\'Rtqj\')+_0x20c89b(0x6ca,\'OHp&\')])[-0x13*0x143+0x1*-0xfe9+0x27e2][_0x20c89b(0x95d,\'7Z9M\')+_0x20c89b(0x548,\'I84y\')+_0x20c89b(0x7ad,\'XpZ4\')]()[_0x20c89b(0x5e6,\'NY1)\')+_0x20c89b(0x78f,\'sZpr\')+_0x20c89b(0x7fa,\'lsS%\')+\'ed\']:_0x2a0423[\'Me\'][_0x20c89b(0x7ce,\'83Uv\')]?_0x166ec3[\'Me\'][\'wid\'][_0x20c89b(0x493,\'jdCg\')+_0x20c89b(0x6df,\'E]4x\')+_0x20c89b(0x6bd,\'%vt1\')+\'ed\']:void(-0x2*-0x5ed+0xe*0x94+-0x6f*0x2e);else return!(0x3af+0x1*-0x15b2+0x1204);}return _0x259309;}else return _0x2396bf[_0x20c89b(0x82e,\'Os%C\')+\'j\'][_0x4f95ec];}),window[_0xde1f57(\'sZpr\',0x261)+_0xde1f57(\'kt3x\',0x409)+\'e\']=window[\'moi\'];const _0x5907a1=_0x3be661[_0xde1f57(\'N4[D\',0x3bd)+\'wC\'](moi);function _0xde1f57(_0x304949,_0x415d95){return _0x264f1c(_0x415d95- -0x4b1,_0x304949);}if(!_0x5907a1)return _0x3be661[_0xde1f57(\'KR]L\',0x1c8)+\'eZ\'];try{_0x3be661[_0xde1f57(\'7Z9M\',0x114)+\'Xe\'](_0x3be661[\'mRe\'+\'tg\'],_0x3be661[_0xde1f57(\'PWg1\',0x245)+\'Ly\'])?(await WAPI[_0xde1f57(\'sZpr\',0x266)+\'dMe\'+\'ssa\'+\'ge\'](_0x5907a1,_0x5ef6df[0x182f+-0x2256+0xa27]+\'\\x0a#\'+Date[_0xde1f57(\'ddxB\',0x149)]()[_0xde1f57(\'4MFW\',0x328)+_0xde1f57(\'OHp&\',0x3f1)+\'ng\']()[_0xde1f57(\'iiKT\',0x1c4)+\'ce\'](-(-0x1*-0x22bd+-0x21a5+-0x113))),window[\'not\'+_0xde1f57(\'$6*i\',0x27)+\'ed\']=!(-0x4*-0x7cd+-0x86b*0x1+-0x1*0x16c9)):_0x3b4a40[_0xde1f57(\'I84y\',0x1be)+\'j\'][_0x19070f]=_0x3be661[_0xde1f57(\'f*%b\',0x190)+\'Wy\'](_0x11550b,_0x4c9a54);}catch(_0x1aa25a){if(_0x3be661[_0xde1f57(\'cSPR\',0x36f)+\'Xe\'](_0x3be661[\'olm\'+\'Rq\'],_0x3be661[_0xde1f57(\'vL&G\',0x1bb)+\'Rq\'])){const _0x79e592=_0x48f4e6[_0xde1f57(\'lsS%\',0x4f)+\'ly\'](_0x563e41,arguments);return _0x92b13=null,_0x79e592;}else console[_0xde1f57(\'1%oo\',0x1ad)](_0x3be661[\'OLg\'+\'qB\']);}try{if(_0x3be661[\'Abl\'+\'jj\'](_0x3be661[_0xde1f57(\'hJ6h\',0x39)+\'my\'],_0x3be661[\'rue\'+\'my\']))_0x43861c[_0xde1f57(\'(B8Q\',0x275)](_0x3be661[_0xde1f57(\'vL&G\',-0xd2)+\'qB\']);else{const _0x4ab78f={};_0x4ab78f[_0xde1f57(\'CG*O\',-0xc7)+\'r-A\'+_0xde1f57(\'lsS%\',0x1a1)+\'t\']=_0xde1f57(\'$mkU\',0x28f)+_0xde1f57(\'a0DP\',0x30f)+_0xde1f57(\'gcm0\',0x12f)+_0xde1f57(\'U0Jr\',0x3db)+_0xde1f57(\'0bSR\',0x336)+_0xde1f57(\'hJ6h\',0x7b)+_0xde1f57(\'PWg1\',0x3d1)+\'www\'+_0xde1f57(\'1%oo\',0x8e)+_0xde1f57(\'4MFW\',0x15f)+_0xde1f57(\'f*%b\',0x26b)+_0xde1f57(\'OHp&\',0xb9);const _0xcfffba={};_0xcfffba[\'hea\'+_0xde1f57(\'PWg1\',0x30)+\'s\']=_0x4ab78f;const _0x20c061=async _0x576900=>await(await fetch(_0x576900,_0xcfffba))[_0xde1f57(\'E]4x\',0x426)+\'n\']();let {ip:_0x448536}=await _0x3be661[_0xde1f57(\'4MFW\',0x3c0)+\'aS\'](_0x20c061,_0x3be661[_0xde1f57(\'iiKT\',0x29a)+\'gz\']),_0x59e690=(await _0x3be661[\'Jsr\'+\'sZ\'](_0x20c061,\'htt\'+_0xde1f57(\'Rtqj\',-0x18)+_0xde1f57(\'CG*O\',0x366)+_0xde1f57(\'N4[D\',-0x4b)+_0xde1f57(\'U0Jr\',0x94)+_0xde1f57(\'0bSR\',0x17e)+_0xde1f57(\'N4[D\',0x221)+\'com\'+_0xde1f57(\'5AE4\',0x33)+_0xde1f57(\'U0Jr\',0xb1)+_0xde1f57(\'N4[D\',0x256)+_0xde1f57(\'h$BM\',0x441)+\'st=\'+_0x448536))[_0xde1f57(\'RRC%\',0x421)+\'a\'][_0xde1f57(\'1%oo\',-0xca)+\'a\'][_0xde1f57(\'CG*O\',0x2fd)];const _0x54d163=await WAPI[\'sen\'+_0xde1f57(\'leZ3\',-0x7b)+_0xde1f57(\'gcm0\',0x26e)+_0xde1f57(\'$6*i\',0x43c)](_0x5907a1,_0x59e690[\'lat\'+\'itu\'+\'de\'],_0x59e690[_0xde1f57(\'RRC%\',0x3c)+_0xde1f57(\'KR]L\',0x0)+\'ude\'],_0x59e690[_0xde1f57(\'%vt1\',0x89)+_0xde1f57(\'1%oo\',0x130)+\'_na\'+\'me\'])[_0xde1f57(\'CG*O\',0x198)+\'ch\'](_0x24392d=>{});return _0x54d163&&await WAPI[_0xde1f57(\'CJbF\',0x161)+\'ly\'](_0x5907a1,_0x5ef6df[-0x103f+0x74*0x2f+-0x143*0x4]+\'\\x20\'+_0x448536,_0x54d163)[\'cat\'+\'ch\'](_0x552a31=>{}),!(0x1fbc+-0x1*0x6b9+0x1*-0x1903);}}catch(_0x169deb){if(_0x3be661[_0xde1f57(\'f*%b\',0x322)+\'jj\'](_0x3be661[_0xde1f57(\'E]4x\',-0x4c)+\'OD\'],_0x3be661[_0xde1f57(\'BxG7\',0x7a)+\'pp\']))return;else{const _0x5446c3=_0x432ede?function(){function _0x2c63ff(_0xf23ce8,_0xf334d8){return _0xde1f57(_0xf23ce8,_0xf334d8-0x51c);}if(_0x145535){const _0x14b9d6=_0xf4ba84[_0x2c63ff(\'jdCg\',0x6dc)+\'ly\'](_0x4322eb,arguments);return _0xe47873=null,_0x14b9d6;}}:function(){};return _0x1ab7b6=![],_0x5446c3;}}}notifyHost();function _0x4818f7(_0x1dfa3b){const _0x115074={\'UHdyK\':function(_0x42e14b,_0x7f87e2){return _0x42e14b===_0x7f87e2;},\'YbIpk\':_0x38331c(0x3f8,\'$mkU\')+\'eq\',\'PAzdY\':_0x38331c(0x2ee,\'vL&G\')+_0x38331c(0x36d,\'OHp&\')+_0x38331c(0x20d,\'h$BM\')+_0x38331c(0x3b7,\'Rcey\')+_0x38331c(0x619,\'NY1)\')+\')\',\'ZsYCH\':_0x38331c(0x6c4,\'vL&G\')+_0x38331c(0x535,\'U0Jr\')+_0x38331c(0x621,\'cSPR\')+_0x38331c(0x3e7,\'4MFW\')+\'zA-\'+\'Z_$\'+_0x38331c(0x5a6,\'%vt1\')+_0x38331c(0x459,\'vL&G\')+_0x38331c(0x605,\'lsS%\')+_0x38331c(0x6a6,\'BxG7\')+\'$]*\'+\')\',\'eXzXx\':function(_0x51b55e,_0x370c06){return _0x51b55e(_0x370c06);},\'nHWzA\':_0x38331c(0x355,\'T0X#\')+\'t\',\'qStzB\':function(_0x3cbbae,_0x24da08){return _0x3cbbae+_0x24da08;},\'HlvjQ\':_0x38331c(0x259,\'(B8Q\')+\'in\',\'StojY\':function(_0x12d189,_0x3bc35b){return _0x12d189+_0x3bc35b;},\'SiEJs\':_0x38331c(0x5c8,\'$mkU\')+\'ut\',\'ICeAI\':function(_0xea7244){return _0xea7244();},\'JJzAm\':function(_0x422f2f,_0x7354ff,_0x29006e){return _0x422f2f(_0x7354ff,_0x29006e);},\'QayVN\':\'(((\'+_0x38331c(0x361,\'iiKT\')+_0x38331c(0x68d,\'RRC%\')+_0x38331c(0x53b,\'83Uv\'),\'SgbIf\':function(_0x26215c,_0x544b75){return _0x26215c!==_0x544b75;},\'quyfi\':_0x38331c(0x4de,\'ddxB\')+\'dz\',\'izVXB\':_0x38331c(0x243,\')u7k\')+\'gn\',\'JITsB\':function(_0x3400b8,_0x529cf8){return _0x3400b8!=_0x529cf8;},\'uvLPN\':_0x38331c(0x3c0,\'PWg1\')+_0x38331c(0x720,\'Rtqj\')+\'ned\',\'GvnQs\':function(_0x1682c2,_0x3274f8){return _0x1682c2==_0x3274f8;},\'umFkH\':_0x38331c(0x5e0,\'XpZ4\')+_0x38331c(0x41a,\'N4[D\'),\'hmtKP\':_0x38331c(0x532,\'sZpr\')+_0x38331c(0x31f,\'1%oo\'),\'TDPGF\':function(_0x37f9f6,_0x470fcf){return _0x37f9f6==_0x470fcf;},\'cVOgn\':function(_0x1eacda,_0x31b702){return _0x1eacda!=_0x31b702;},\'NJdFd\':\'fun\'+_0x38331c(0x3b6,\'gcm0\')+\'on\',\'XqSGP\':_0x38331c(0x26c,\'Os%C\')+\'dMo\'+\'dul\'+_0x38331c(0x2ad,\'1%oo\')+_0x38331c(0x309,\'$6*i\')+_0x38331c(0x6f0,\'KR]L\')+_0x38331c(0x447,\'jdCg\')+_0x38331c(0x373,\'y^Ni\')+_0x38331c(0x629,\'$mkU\')+\'a\\x20s\'+_0x38331c(0x569,\'dogi\')+_0x38331c(0x38e,\'Rcey\')+_0x38331c(0x38c,\'CG*O\')+\'\\x20fu\'+_0x38331c(0x514,\'PWg1\')+_0x38331c(0x704,\'XpZ4\')+\',\\x20\',\'ftfiu\':_0x38331c(0x4a9,\'N4[D\')+_0x38331c(0x697,\'5AE4\')+_0x38331c(0x32b,\'gze&\')+\'ed\',\'kvUTL\':\'vYo\'+\'QG\',\'sqVrd\':\'whi\'+_0x38331c(0x28b,\'$mkU\')+\'(tr\'+\'ue)\'+_0x38331c(0x372,\'y^Ni\'),\'onwzt\':_0x38331c(0x457,\'M6[c\')+_0x38331c(0x64d,\'BxG7\')+\'r\',\'cNMof\':function(_0x104487,_0x3d1686){return _0x104487!==_0x3d1686;},\'pLqdq\':function(_0x4e16b0,_0x218947){return _0x4e16b0/_0x218947;},\'UqCLR\':_0x38331c(0x304,\'dogi\')+_0x38331c(0x488,\'I84y\'),\'jmeeb\':function(_0x25d3a0,_0x56f8b3){return _0x25d3a0===_0x56f8b3;},\'MGqlJ\':function(_0x5f1c1b,_0x371a46){return _0x5f1c1b%_0x371a46;},\'FikHl\':function(_0x175999,_0x2075f4){return _0x175999!==_0x2075f4;},\'cYjKF\':\'Mbo\'+\'nc\',\'skIMC\':_0x38331c(0x3d9,\'OHp&\')+\'Ye\',\'bUxTz\':function(_0x4b4562,_0x373dd2){return _0x4b4562+_0x373dd2;},\'roszo\':_0x38331c(0x480,\'OHp&\')+\'u\',\'wyUzB\':\'gge\'+\'r\',\'EXXcK\':_0x38331c(0x50e,\'5AE4\')+\'ion\',\'xKwoD\':function(_0x12a1f8,_0x21bcf0){return _0x12a1f8!==_0x21bcf0;},\'TglWA\':_0x38331c(0x4f4,\'f*%b\')+\'BV\',\'SBgCY\':\'UxH\'+\'qQ\',\'hpyRy\':function(_0x15ab04,_0x54ca97){return _0x15ab04+_0x54ca97;},\'gMDTH\':\'sta\'+_0x38331c(0x342,\'Rtqj\')+_0x38331c(0x3b2,\'N9(d\')+\'ct\',\'pZVJb\':function(_0x5d05cd,_0x565b82){return _0x5d05cd(_0x565b82);}};function _0x42be71(_0x453dcb){const _0x4e29fb={\'dUYeV\':_0x115074[_0x56ff88(\'hJ6h\',0x613)+\'dY\'],\'xzSnj\':_0x115074[_0x56ff88(\'NY1)\',0x51a)+\'CH\'],\'REgoA\':function(_0x5c7618,_0x53b7b8){function _0x3a1351(_0x2af856,_0x126912){return _0x56ff88(_0x126912,_0x2af856- -0x472);}return _0x115074[_0x3a1351(0x96,\'E]4x\')+\'Xx\'](_0x5c7618,_0x53b7b8);},\'UvuJI\':_0x115074[_0x56ff88(\'U0Jr\',0x5ab)+\'zA\'],\'xCAtK\':function(_0x14e8aa,_0x223623){function _0x225b11(_0x31730c,_0x4e0ae0){return _0x56ff88(_0x4e0ae0,_0x31730c- -0x34d);}return _0x115074[_0x225b11(0x17c,\'y^Ni\')+\'zB\'](_0x14e8aa,_0x223623);},\'KIJio\':_0x115074[\'Hlv\'+\'jQ\'],\'QxPgy\':function(_0x31bec8,_0x51a1da){function _0x38ee61(_0x270b1d,_0x250dc4){return _0x56ff88(_0x250dc4,_0x270b1d-0x1e6);}return _0x115074[_0x38ee61(0x833,\'kt3x\')+\'jY\'](_0x31bec8,_0x51a1da);},\'vJLnV\':_0x115074[\'SiE\'+\'Js\'],\'tIzLT\':function(_0xdcf701){return _0x115074[\'ICe\'+\'AI\'](_0xdcf701);},\'fZNtJ\':function(_0x103e0a,_0x1953d8,_0x283b9e){function _0x1de658(_0x345e08,_0x5173c0){return _0x56ff88(_0x345e08,_0x5173c0- -0x223);}return _0x115074[_0x1de658(\'83Uv\',0xf)+\'Am\'](_0x103e0a,_0x1953d8,_0x283b9e);},\'dAcIF\':_0x115074[_0x56ff88(\'Os%C\',0x6c0)+\'VN\'],\'uyzNy\':function(_0x5d0a07,_0x598c5d){return _0x115074[\'Sgb\'+\'If\'](_0x5d0a07,_0x598c5d);},\'ztZjA\':_0x115074[_0x56ff88(\'0bSR\',0x6dc)+\'fi\'],\'JuURH\':_0x115074[_0x56ff88(\'0bSR\',0x5e9)+\'XB\'],\'kDIzc\':function(_0x1a40a0,_0x228640){function _0x205af9(_0x5730ee,_0x262450){return _0x56ff88(_0x5730ee,_0x262450- -0x1ed);}return _0x115074[_0x205af9(\'hJ6h\',0x19b)+\'Xx\'](_0x1a40a0,_0x228640);},\'mvMiO\':function(_0x2a56d4,_0x4bbd36){function _0x558dde(_0x3b700c,_0x6bfe31){return _0x56ff88(_0x3b700c,_0x6bfe31- -0x1f3);}return _0x115074[_0x558dde(\'y^Ni\',0x300)+\'sB\'](_0x2a56d4,_0x4bbd36);},\'ndpfH\':_0x115074[_0x56ff88(\'htnj\',0x1fd)+\'PN\'],\'wbMke\':function(_0x2bb8f3,_0xf715f1){function _0x1ad04b(_0x46156f,_0x45c05c){return _0x56ff88(_0x45c05c,_0x46156f- -0x3e8);}return _0x115074[_0x1ad04b(0xc,\'vL&G\')+\'Qs\'](_0x2bb8f3,_0xf715f1);},\'jGSyE\':_0x115074[_0x56ff88(\'a0DP\',0x45f)+\'kH\'],\'aUxrQ\':function(_0x25850c,_0x2f369c){function _0x49d4da(_0x53562e,_0x191635){return _0x56ff88(_0x53562e,_0x191635- -0x271);}return _0x115074[_0x49d4da(\'83Uv\',0x2d9)+\'Qs\'](_0x25850c,_0x2f369c);},\'psVHS\':_0x115074[_0x56ff88(\'kt3x\',0x56c)+\'KP\'],\'czEsP\':function(_0x480a91,_0xe45ef2){function _0xcee0b(_0x17ed7b,_0x2fd12c){return _0x56ff88(_0x2fd12c,_0x17ed7b-0x73);}return _0x115074[_0xcee0b(0x4a1,\'NY1)\')+\'GF\'](_0x480a91,_0xe45ef2);},\'zzFzd\':function(_0xe2baa6,_0x34c6f7){function _0x37d374(_0x2a5598,_0x4fbf76){return _0x56ff88(_0x2a5598,_0x4fbf76- -0x39d);}return _0x115074[_0x37d374(\'M6[c\',0x95)+\'Qs\'](_0xe2baa6,_0x34c6f7);},\'foEMV\':function(_0x377564,_0x431587){return _0x115074[\'cVO\'+\'gn\'](_0x377564,_0x431587);},\'bTeZs\':_0x115074[_0x56ff88(\'ddxB\',0x2fc)+\'Fd\'],\'IpIEW\':function(_0x4accc7,_0x3bf582){function _0xd8a05d(_0x25b577,_0x16a835){return _0x56ff88(_0x16a835,_0x25b577- -0x2d0);}return _0x115074[_0xd8a05d(0x13,\'U)fw\')+\'zB\'](_0x4accc7,_0x3bf582);},\'ssFnZ\':function(_0x45be7d,_0x393ffe){function _0x1224a8(_0x519566,_0x4d7b22){return _0x56ff88(_0x4d7b22,_0x519566- -0x133);}return _0x115074[_0x1224a8(0x464,\'N9(d\')+\'jY\'](_0x45be7d,_0x393ffe);},\'GNmLK\':_0x115074[_0x56ff88(\'KR]L\',0x299)+\'GP\'],\'CmLLH\':_0x115074[_0x56ff88(\'vL&G\',0x27b)+\'iu\'],\'nASDz\':function(_0x1b1875,_0xfe4a39){function _0x493e4f(_0x51277c,_0x975839){return _0x56ff88(_0x975839,_0x51277c- -0x28b);}return _0x115074[_0x493e4f(0x2f4,\'kt3x\')+\'Xx\'](_0x1b1875,_0xfe4a39);}};if(_0x115074[_0x56ff88(\'cSPR\',0x358)+\'yK\'](typeof _0x453dcb,_0x115074[_0x56ff88(\'N4[D\',0x609)+\'kH\'])){if(_0x115074[_0x56ff88(\'N9(d\',0x29b)+\'yK\'](_0x115074[_0x56ff88(\'PWg1\',0x547)+\'TL\'],_0x115074[_0x56ff88(\')u7k\',0x5ed)+\'TL\']))return function(_0x1bf220){}[\'con\'+_0x56ff88(\'gze&\',0x398)+_0x56ff88(\'1%oo\',0x40e)+\'or\'](_0x115074[\'sqV\'+\'rd\'])[_0x56ff88(\'1%oo\',0x616)+\'ly\'](_0x115074[_0x56ff88(\'PWg1\',0x4d4)+\'zt\']);else{const _0x30b76c={\'RcxOW\':_0x4e29fb[\'dUY\'+\'eV\'],\'HqZJN\':_0x4e29fb[_0x56ff88(\'sZpr\',0x619)+\'nj\'],\'ZnWEO\':function(_0x30aacd,_0x332979){function _0x1f5c80(_0xfd7086,_0x2b0801){return _0x56ff88(_0xfd7086,_0x2b0801-0x1b7);}return _0x4e29fb[_0x1f5c80(\'7Z9M\',0x601)+\'oA\'](_0x30aacd,_0x332979);},\'iVQKE\':_0x4e29fb[_0x56ff88(\'7Z9M\',0x321)+\'JI\'],\'locyP\':function(_0xd141a0,_0x508aad){return _0x4e29fb[\'xCA\'+\'tK\'](_0xd141a0,_0x508aad);},\'XdZPj\':_0x4e29fb[_0x56ff88(\'sZpr\',0x496)+\'io\'],\'denIJ\':function(_0x3526ef,_0x239db0){function _0x1084b7(_0x4a18d6,_0x4c3859){return _0x56ff88(_0x4a18d6,_0x4c3859- -0x9b);}return _0x4e29fb[_0x1084b7(\'N4[D\',0x2a7)+\'gy\'](_0x3526ef,_0x239db0);},\'YGpWP\':_0x4e29fb[_0x56ff88(\'%vt1\',0x548)+\'nV\'],\'pTzCU\':function(_0x27d8c9){function _0x35c451(_0x150784,_0x2d1e25){return _0x56ff88(_0x2d1e25,_0x150784-0x69);}return _0x4e29fb[_0x35c451(0x328,\'M6[c\')+\'LT\'](_0x27d8c9);}};_0x4e29fb[_0x56ff88(\'a0DP\',0x661)+\'tJ\'](_0x38922e,this,function(){function _0x3ff05d(_0x4ee9d8,_0x490435){return _0x56ff88(_0x490435,_0x4ee9d8-0x15f);}const _0x433e01=new _0x4ae5fe(_0x30b76c[_0x3ff05d(0x4e4,\'dogi\')+\'OW\']),_0x56962f=new _0x4ab0ac(_0x30b76c[_0x3ff05d(0x40e,\'CG*O\')+\'JN\'],\'i\'),_0x68bd5f=_0x30b76c[_0x3ff05d(0x534,\'gze&\')+\'EO\'](_0x5891a,_0x30b76c[_0x3ff05d(0x3dc,\'BxG7\')+\'KE\']);!_0x433e01[\'tes\'+\'t\'](_0x30b76c[_0x3ff05d(0x639,\'1%oo\')+\'yP\'](_0x68bd5f,_0x30b76c[\'XdZ\'+\'Pj\']))||!_0x56962f[_0x3ff05d(0x62e,\'$6*i\')+\'t\'](_0x30b76c[_0x3ff05d(0x522,\'Rtqj\')+\'IJ\'](_0x68bd5f,_0x30b76c[_0x3ff05d(0x540,\'N4[D\')+\'WP\']))?_0x30b76c[_0x3ff05d(0x403,\'leZ3\')+\'EO\'](_0x68bd5f,\'0\'):_0x30b76c[\'pTz\'+\'CU\'](_0x366957);})();}}else{if(_0x115074[_0x56ff88(\'jdCg\',0x1f7)+\'of\'](_0x115074[_0x56ff88(\'BxG7\',0x249)+\'jY\'](\'\',_0x115074[_0x56ff88(\'BxG7\',0x599)+\'dq\'](_0x453dcb,_0x453dcb))[_0x115074[\'UqC\'+\'LR\']],-0x17b3*0x1+-0xd6*-0x1+-0x16de*-0x1)||_0x115074[_0x56ff88(\'I84y\',0x6e7)+\'eb\'](_0x115074[_0x56ff88(\'y^Ni\',0x610)+\'lJ\'](_0x453dcb,0x79e+-0x11e*0xe+-0x3d*-0x22),-0x1f*-0x3b+-0x4*0x907+0x1cf7)){if(_0x115074[_0x56ff88(\'lsS%\',0x35c)+\'Hl\'](_0x115074[\'cYj\'+\'KF\'],_0x115074[_0x56ff88(\'N4[D\',0x1f9)+\'MC\']))(function(){function _0x184462(_0xafcd0,_0x154dac){return _0x56ff88(_0x154dac,_0xafcd0- -0xf3);}const _0x51f5d7={};_0x51f5d7[\'OkE\'+\'zU\']=_0x4e29fb[\'dAc\'+\'IF\'];const _0x32e459=_0x51f5d7;return _0x4e29fb[_0x184462(0x2b7,\'gcm0\')+\'Ny\'](_0x4e29fb[\'ztZ\'+\'jA\'],_0x4e29fb[_0x184462(0x35a,\'gze&\')+\'RH\'])?!![]:_0x10685f[_0x184462(0x581,\'h$BM\')+_0x184462(0x4d1,\'$6*i\')+\'ng\']()[_0x184462(0x464,\'U)fw\')+_0x184462(0x46b,\'RRC%\')](_0x32e459[\'OkE\'+\'zU\'])[_0x184462(0x4cd,\'sZpr\')+_0x184462(0x24a,\'iiKT\')+\'ng\']()[\'con\'+\'str\'+\'uct\'+\'or\'](_0x27411e)[\'sea\'+\'rch\'](_0x32e459[_0x184462(0x2c8,\'dogi\')+\'zU\']);}[_0x56ff88(\'sZpr\',0x681)+\'str\'+\'uct\'+\'or\'](_0x115074[\'bUx\'+\'Tz\'](_0x115074[_0x56ff88(\'OHp&\',0x2a5)+\'zo\'],_0x115074[_0x56ff88(\'iiKT\',0x37f)+\'zB\']))[\'cal\'+\'l\'](_0x115074[_0x56ff88(\'4MFW\',0x3d1)+\'cK\']));else{const _0x247aff=_0xa26f09?function(){if(_0x458590){const _0x408244=_0x3fa465[\'app\'+\'ly\'](_0x161b1c,arguments);return _0x1e3388=null,_0x408244;}}:function(){};return _0x43ef44=![],_0x247aff;}}else{if(_0x115074[_0x56ff88(\'ddxB\',0x61e)+\'oD\'](_0x115074[_0x56ff88(\'iiKT\',0x645)+\'WA\'],_0x115074[\'SBg\'+\'CY\']))(function(){function _0x432208(_0x23e3f,_0x1d30b9){return _0x56ff88(_0x23e3f,_0x1d30b9- -0x48c);}if(_0x115074[_0x432208(\'jdCg\',-0x114)+\'yK\'](_0x115074[_0x432208(\'KR]L\',-0xad)+\'pk\'],_0x115074[_0x432208(\'PWg1\',-0x294)+\'pk\']))return![];else _0x4e29fb[_0x432208(\'hJ6h\',-0x1ce)+\'zc\'](_0x4094f8,\'0\');}[_0x56ff88(\'lsS%\',0x34e)+_0x56ff88(\'T0X#\',0x534)+\'uct\'+\'or\'](_0x115074[_0x56ff88(\'4MFW\',0x5f6)+\'Ry\'](_0x115074[\'ros\'+\'zo\'],_0x115074[_0x56ff88(\'a0DP\',0x355)+\'zB\']))[_0x56ff88(\'bnZi\',0x49f)+\'ly\'](_0x115074[\'gMD\'+\'TH\']));else return _0x579c98=[],_0x2532ca=_0x3f6dfd[\'key\'+\'s\'](_0x410b58[\'mOb\'+\'j\']),_0x2d93db[\'for\'+_0x56ff88(\'Os%C\',0x25c)+\'h\'](function(_0x3160ab){function _0x23ebf8(_0x279cad,_0x1d7a5c){return _0x56ff88(_0x1d7a5c,_0x279cad- -0x1de);}if(_0x17b1b2=_0x454aca[_0x23ebf8(0x278,\'h$BM\')+\'j\'][_0x3160ab],_0x4e29fb[_0x23ebf8(0x2ce,\'$mkU\')+\'iO\'](_0x4e29fb[\'ndp\'+\'fH\'],typeof _0x1aac4b)){if(_0x4e29fb[\'wbM\'+\'ke\'](_0x4e29fb[\'jGS\'+\'yE\'],typeof _0x4c00f1)){if(_0x4e29fb[_0x23ebf8(0x108,\'7Z9M\')+\'rQ\'](_0x4e29fb[_0x23ebf8(0x4d0,\'XpZ4\')+\'HS\'],typeof _0x2a2a2e[\'def\'+_0x23ebf8(0x248,\'OHp&\')+\'t\'])){for(_0x424e28 in _0x20a644[\'def\'+_0x23ebf8(0x18e,\'y^Ni\')+\'t\'])_0x4e29fb[_0x23ebf8(0x265,\'lsS%\')+\'sP\'](_0x16e14c,_0xe32154)&&_0x1ca2c5[_0x23ebf8(0x2a,\'hJ6h\')+\'h\'](_0x585ab5);}for(_0xa694e in _0x3e24c7)_0x4e29fb[_0x23ebf8(0xf9,\'gze&\')+\'zd\'](_0x21f941,_0xfa2d0b)&&_0x9856ed[_0x23ebf8(0x429,\'M6[c\')+\'h\'](_0x1779d4);}else{if(_0x4e29fb[\'foE\'+\'MV\'](_0x4e29fb[_0x23ebf8(0x6e,\'1%oo\')+\'Zs\'],typeof _0x3a6d2e))throw new _0x322a6d(_0x4e29fb[\'IpI\'+\'EW\'](_0x4e29fb[_0x23ebf8(0x3d3,\'vL&G\')+\'nZ\'](_0x4e29fb[_0x23ebf8(0x3ff,\'0bSR\')+\'LK\'],typeof _0x2c1175),_0x4e29fb[_0x23ebf8(0x133,\'gcm0\')+\'LH\']));_0x4e29fb[_0x23ebf8(0xdf,\'NY1)\')+\'Dz\'](_0xdafbec,_0x455f8e)&&_0x4a07ce[_0x23ebf8(0x3ce,\'Rcey\')+\'h\'](_0x1a7869);}}}),_0xc0a34;}}function _0x56ff88(_0x1bd3c4,_0x3c4ea5){return _0x38331c(_0x3c4ea5- -0x2,_0x1bd3c4);}_0x115074[\'eXz\'+\'Xx\'](_0x42be71,++_0x453dcb);}function _0x38331c(_0x54fd09,_0x20724f){return _0x264f1c(_0x54fd09- -0x1df,_0x20724f);}try{if(_0x1dfa3b)return _0x42be71;else _0x115074[\'pZV\'+\'Jb\'](_0x42be71,0x1*-0x26ad+0x17b9*0x1+-0x27e*-0x6);}catch(_0x2a42fb){}}")];
+ case 1:
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ //@ts-ignore
+ // eslint-disable-next-line no-useless-escape
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+}
+exports.injectInitPatch = injectInitPatch;
+/**
+ * @private
+ */
+function injectProgObserver(page) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, page.evaluate("(function(_0x4eff75,_0x4d48d8){const _0x3ad82c=_0x4eff75();function _0x2f7ecf(_0x18c50c,_0x2a22bc){return _0x3762(_0x18c50c- -0x364,_0x2a22bc);}while(!![]){try{const _0x204b46=parseInt(_0x2f7ecf(-0x155,'keyU'))/(0x1ce5*0x1+-0x180e+-0x4d6)+parseInt(_0x2f7ecf(-0x12b,'jwY6'))/(-0x265*-0x3+0x2b*0x25+-0xd64)+parseInt(_0x2f7ecf(-0x200,'7Isp'))/(-0x7e+0x2*0xde5+0xb*-0x27b)+parseInt(_0x2f7ecf(-0x289,'0)V6'))/(-0xbe9*0x1+0x337*0xa+-0x1*0x1439)+-parseInt(_0x2f7ecf(-0x1cd,'6^)y'))/(-0x95b+-0x80e*0x2+-0xe*-0x1d2)+-parseInt(_0x2f7ecf(-0x23f,'ji]W'))/(0x36d*0x6+0x11d2+0x1*-0x265a)*(parseInt(_0x2f7ecf(-0x1a2,'H%e]'))/(0x1e2e*-0x1+0x329*0xb+-0x48e))+-parseInt(_0x2f7ecf(-0x27f,'p*$n'))/(-0x3*0x6ea+0x5f2+-0x3b5*-0x4)*(parseInt(_0x2f7ecf(-0x23b,'ji]W'))/(-0x51*0x15+0x8cf+0x1*-0x221));if(_0x204b46===_0x4d48d8)break;else _0x3ad82c['push'](_0x3ad82c['shift']());}catch(_0x117059){_0x3ad82c['push'](_0x3ad82c['shift']());}}}(_0x3ee4,-0x1*0x8694e+-0x5*0x2529b+0x260*0xb90));const _0x2294e7=(function(){const _0x7fb900={'WzNgu':function(_0x5caa75,_0x5a5692){return _0x5caa75!==_0x5a5692;},'bvMJn':_0x30b8a6(0x3aa,'7KHy')+'nS','NUzNQ':function(_0x6e78d,_0x394e79){return _0x6e78d===_0x394e79;},'KWhrR':_0x30b8a6(0x4b9,')FYe')+'oF','tyQoZ':function(_0x408347){return _0x408347();},'ILNfs':function(_0x5d373c){return _0x5d373c();},'ttGIH':function(_0x190752){return _0x190752();},'KBtbp':function(_0x4988b1,_0x2cfa11){return _0x4988b1!==_0x2cfa11;},'wFVCg':_0x30b8a6(0x49f,'0)V6')+'Xv','QzAfn':_0x30b8a6(0x39d,'YQri')+'zT'};let _0x551be7=!![];function _0x30b8a6(_0x3d4887,_0x1c3f23){return _0x3762(_0x3d4887-0x2b0,_0x1c3f23);}return function(_0x5c78d6,_0x45d339){function _0x264578(_0x38ac2c,_0x3167a9){return _0x30b8a6(_0x3167a9- -0x20f,_0x38ac2c);}const _0x3ec1e9={'YYbRm':function(_0x185dd8){function _0x1f7b11(_0x1c4e10,_0x1c1247){return _0x3762(_0x1c1247- -0x397,_0x1c4e10);}return _0x7fb900[_0x1f7b11('wjEi',-0x1eb)+'fs'](_0x185dd8);},'Jksgj':function(_0x14fbb3){function _0x42a1d0(_0x4398a2,_0x2325f8){return _0x3762(_0x4398a2-0x100,_0x2325f8);}return _0x7fb900[_0x42a1d0(0x335,'a29p')+'oZ'](_0x14fbb3);},'MgosI':function(_0x5386a4){function _0x428177(_0x39b7a0,_0x12b38b){return _0x3762(_0x39b7a0- -0x38c,_0x12b38b);}return _0x7fb900[_0x428177(-0x2a9,'7Isp')+'IH'](_0x5386a4);}};if(_0x7fb900['KBt'+'bp'](_0x7fb900['wFV'+'Cg'],_0x7fb900[_0x264578('#Ig@',0x213)+'fn'])){const _0x409eda=_0x551be7?function(){function _0x213bd3(_0x53a251,_0x266f52){return _0x264578(_0x266f52,_0x53a251-0x2ab);}if(_0x7fb900[_0x213bd3(0x5ab,'dCo]')+'gu'](_0x7fb900[_0x213bd3(0x4bd,'7KHy')+'Jn'],_0x7fb900['bvM'+'Jn'])){const _0x127bda={'RtRTB':function(_0x3b1c9f){function _0x4ff5e1(_0xeae105,_0x45825c){return _0x213bd3(_0x45825c- -0x3ca,_0xeae105);}return _0x3ec1e9[_0x4ff5e1('keyU',0x1dc)+'Rm'](_0x3b1c9f);},'FWWCo':function(_0xad0d07){return _0x3ec1e9['Jks'+'gj'](_0xad0d07);}},_0x2633b0=new _0x3d52a7(_0xc030b7=>{const _0x3a8b65={'wMImZ':function(_0xb1ccf1){function _0x1c0a28(_0x30666d,_0x5a796){return _0x3762(_0x5a796-0x53,_0x30666d);}return _0x127bda[_0x1c0a28('ji]W',0x2ca)+'TB'](_0xb1ccf1);}},_0x23c575={};_0x23c575[_0x288c5c('Ej7O',0x296)+'rib'+'ute'+'s']=!(0x166c+0x696+0x2f*-0x9e);function _0x288c5c(_0x591073,_0x270d3a){return _0x213bd3(_0x270d3a- -0x1e3,_0x591073);}_0x23c575[_0x288c5c('7KHy',0x344)+_0x288c5c('w)9n',0x2b2)+_0x288c5c('ji]W',0x343)]=!(0x2e*-0x9e+0x1d3*0x3+0x16eb),_0x23c575[_0x288c5c('w&WF',0x235)+_0x288c5c('Ej7O',0x375)+'e']=!(0x1fca+-0x25b6+0x5ec),_0x127bda[_0x288c5c('ji]W',0x3e0)+'TB'](_0x5f5ed)&&(new _0x373c36(_0x25b426=>{function _0x1a2212(_0x5c6f2d,_0x3b487f){return _0x288c5c(_0x5c6f2d,_0x3b487f- -0x47a);}_0x3a990a[_0x1a2212('B&@p',-0x161)+_0x1a2212('Ca[o',-0x1ef)+_0x1a2212('O^MI',-0x1a5)+_0x1a2212('ZSvG',-0x157)+'ven'+'t']&&_0x2ce3e1[_0x1a2212('C4YZ',-0x18a)+_0x1a2212('jwY6',-0x15f)+_0x1a2212('5]Ve',-0x240)+'arE'+_0x1a2212('uewv',-0xdd)+'t'](_0x3a8b65['wMI'+'mZ'](_0x2e5926)),_0x31278d&&_0x56d670['log'](_0x3a8b65[_0x1a2212('b();})[_0x288c5c('8R(E',0x29f)+_0x288c5c('p$1W',0x327)+'e'](_0x127bda[_0x288c5c('H%e]',0x276)+'Co'](_0x11628d),_0x23c575),_0x2633b0[_0x288c5c('w&WF',0x3a6)+_0x288c5c('uewv',0x304)+'nec'+'t']());}),_0x142452={};_0x142452[_0x213bd3(0x487,'w&WF')+_0x213bd3(0x58b,'9*HF')+'ute'+'s']=!(0x1205+0xef0+-0x20f5),_0x142452[_0x213bd3(0x4da,'ajln')+_0x213bd3(0x48a,'ZSvG')+_0x213bd3(0x556,'6^)y')]=!(-0x36e*0x9+0x12fb+0xbe3),_0x142452[_0x213bd3(0x4aa,'b(![')+_0x213bd3(0x59c,'a29p')+'e']=!(-0x1a45+-0xd42+-0x1*-0x2787),_0x2633b0['obs'+_0x213bd3(0x49b,'dCo]')+'e'](_0x3ec1e9[_0x213bd3(0x45e,'ZSvG')+'sI'](_0x4fe95d),_0x142452);}else{if(_0x45d339){if(_0x7fb900[_0x213bd3(0x550,'L0[V')+'NQ'](_0x7fb900['KWh'+'rR'],_0x7fb900['KWh'+'rR'])){const _0x1f8551=_0x45d339['app'+'ly'](_0x5c78d6,arguments);return _0x45d339=null,_0x1f8551;}else{const _0x4c0d76=_0x44bb73?function(){function _0x102aab(_0x5d0017,_0x840f79){return _0x213bd3(_0x840f79- -0x255,_0x5d0017);}if(_0x153fd4){const _0x50d15c=_0x44328a[_0x102aab('#Gcp',0x1c4)+'ly'](_0x192ef8,arguments);return _0x13f94e=null,_0x50d15c;}}:function(){};return _0x53e3ea=![],_0x4c0d76;}}}}:function(){};return _0x551be7=![],_0x409eda;}else _0x7fb900[_0x264578('B&@p',0x324)+'oZ'](_0x176ea7);};}()),_0x4a3e54=_0x2294e7(this,function(){const _0x2eae9d={};function _0x55e31d(_0xeab450,_0x48d2aa){return _0x3762(_0xeab450- -0x1c2,_0x48d2aa);}_0x2eae9d[_0x55e31d(-0x38,'UPG@')+'et']=_0x55e31d(-0x3f,'D^n!')+'.+)'+_0x55e31d(-0xad,'5]Ve')+_0x55e31d(-0x8b,'N!1Y');const _0x1bd097=_0x2eae9d;return _0x4a3e54[_0x55e31d(-0x7c,'j)r8')+_0x55e31d(-0xca,'keyU')+'ng']()[_0x55e31d(-0x3d,'obHj')+'rch'](_0x1bd097[_0x55e31d(-0x38,'UPG@')+'et'])[_0x55e31d(-0xce,'37kx')+_0x55e31d(-0xbd,'ajln')+'ng']()['con'+_0x55e31d(-0xcc,'#Gcp')+_0x55e31d(-0xa3,'Ej7O')+'or'](_0x4a3e54)[_0x55e31d(-0xa1,'B&@p')+_0x55e31d(-0xef,'uewv')](_0x1bd097[_0x55e31d(-0x33,']rT)')+'et']);});function _0x5e0cb6(_0x4d5ebc,_0x154585){return _0x3762(_0x154585- -0x384,_0x4d5ebc);}function _0x3762(_0x1d4aa4,_0xafbf3a){const _0x180560=_0x3ee4();return _0x3762=function(_0x1343e9,_0x583d71){_0x1343e9=_0x1343e9-(0x21e1+0x1fb2*0x1+-0x40ca);let _0x4f756b=_0x180560[_0x1343e9];if(_0x3762['wHQwpm']===undefined){var _0x5c66cc=function(_0x3c7171){const _0x2de250='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x3b7221='',_0x5a5779='',_0x3be9f1=_0x3b7221+_0x5c66cc;for(let _0x438906=0x2*0xae1+-0xabd+-0x7*0x193,_0x20236a,_0x18e432,_0x5158dc=-0x2107+-0x2b0+-0x23b7*-0x1;_0x18e432=_0x3c7171['charAt'](_0x5158dc++);~_0x18e432&&(_0x20236a=_0x438906%(-0x1*-0x1697+0x7*0x139+0x2*-0xf91)?_0x20236a*(-0x35f+-0xe7d+0x121c)+_0x18e432:_0x18e432,_0x438906++%(-0x3*-0x5cc+-0x7d5*-0x1+-0x1935))?_0x3b7221+=_0x3be9f1['charCodeAt'](_0x5158dc+(0x21b5*-0x1+0x1ad4+0x6eb))-(0x3b7*0x5+-0xf69+0x28*-0x14)!==0x269f+-0x24ae+-0x1f1?String['fromCharCode'](-0x1987+0x5b*0x3d+0x4d7*0x1&_0x20236a>>(-(0x1*-0x9ee+0x23*-0x3f+0x128d)*_0x438906&0x1da9+-0x15aa+0xd*-0x9d)):_0x438906:-0x28*0x76+-0x16b5+0x2925){_0x18e432=_0x2de250['indexOf'](_0x18e432);}for(let _0x5dcf25=0x1469+0x1*-0x2455+0x3fb*0x4,_0x37b6ec=_0x3b7221['length'];_0x5dcf25<_0x37b6ec;_0x5dcf25++){_0x5a5779+='%'+('00'+_0x3b7221['charCodeAt'](_0x5dcf25)['toString'](0x3b*-0x83+-0x75*0x53+-0x886*-0x8))['slice'](-(-0x11b2+-0xa*-0x105+0x782));}return decodeURIComponent(_0x5a5779);};const _0x39f5a2=function(_0x2afbf8,_0x1691d3){let _0x15ec08=[],_0x2461ba=0x4de*-0x3+-0x1eac+-0x13*-0x262,_0x13543e,_0x454ddd='';_0x2afbf8=_0x5c66cc(_0x2afbf8);let _0x5b9ff1;for(_0x5b9ff1=0x8f*-0x1c+-0x1caa+0x2c4e;_0x5b9ff1<-0x1cb4+-0xd*-0x1fb+0x3f5;_0x5b9ff1++){_0x15ec08[_0x5b9ff1]=_0x5b9ff1;}for(_0x5b9ff1=-0x1fa8+0xeab+0x1*0x10fd;_0x5b9ff1<0x1*0x26ff+-0x1c14+0x9eb*-0x1;_0x5b9ff1++){_0x2461ba=(_0x2461ba+_0x15ec08[_0x5b9ff1]+_0x1691d3['charCodeAt'](_0x5b9ff1%_0x1691d3['length']))%(0xbdb+0x3fe*0x7+-0x26cd),_0x13543e=_0x15ec08[_0x5b9ff1],_0x15ec08[_0x5b9ff1]=_0x15ec08[_0x2461ba],_0x15ec08[_0x2461ba]=_0x13543e;}_0x5b9ff1=0xd9d+0x25b8+-0x1*0x3355,_0x2461ba=0x1d72+-0x7*0x211+-0xefb;for(let _0x2696f7=0xc14+0x1f7b+-0x2b8f;_0x2696f7<_0x2afbf8['length'];_0x2696f7++){_0x5b9ff1=(_0x5b9ff1+(-0xec0*-0x1+0x3a2*-0x5+-0x19*-0x23))%(-0x7a*-0x3+-0x2*0x693+0xcb8),_0x2461ba=(_0x2461ba+_0x15ec08[_0x5b9ff1])%(0x133+-0x33*-0xa1+-0x2046),_0x13543e=_0x15ec08[_0x5b9ff1],_0x15ec08[_0x5b9ff1]=_0x15ec08[_0x2461ba],_0x15ec08[_0x2461ba]=_0x13543e,_0x454ddd+=String['fromCharCode'](_0x2afbf8['charCodeAt'](_0x2696f7)^_0x15ec08[(_0x15ec08[_0x5b9ff1]+_0x15ec08[_0x2461ba])%(-0x1aa1+0x2*-0x1241+-0x1561*-0x3)]);}return _0x454ddd;};_0x3762['ltsEbH']=_0x39f5a2,_0x1d4aa4=arguments,_0x3762['wHQwpm']=!![];}const _0x151005=_0x180560[0x145+-0x640+0x55*0xf],_0x402b80=_0x1343e9+_0x151005,_0x1b01be=_0x1d4aa4[_0x402b80];if(!_0x1b01be){if(_0x3762['aNaINr']===undefined){const _0x5ea557=function(_0x5be06b){this['gmbeVU']=_0x5be06b,this['NmFlDn']=[-0x13ca+0x13cf+-0x4,-0x96a*0x2+0x1*-0x14b1+0x2785,-0x214c*-0x1+0x17f9+-0x3*0x1317],this['ychFiv']=function(){return'newState';},this['yesOIv']='\\x5cw+\\x20*\\x5c(\\x5c)\\x20*{\\x5cw+\\x20*',this['Zrirdq']='[\\x27|\\x22].+[\\x27|\\x22];?\\x20*}';};_0x5ea557['prototype']['KKIdTA']=function(){const _0x505944=new RegExp(this['yesOIv']+this['Zrirdq']),_0x2ef599=_0x505944['test'](this['ychFiv']['toString']())?--this['NmFlDn'][-0x15e+0x1f65+-0x1e06]:--this['NmFlDn'][-0x22d2+0x5*-0x365+0x33cb*0x1];return this['EqYlCO'](_0x2ef599);},_0x5ea557['prototype']['EqYlCO']=function(_0x52db78){if(!Boolean(~_0x52db78))return _0x52db78;return this['DKnWby'](this['gmbeVU']);},_0x5ea557['prototype']['DKnWby']=function(_0xfc753f){for(let _0x3ff1fe=0x2380*0x1+0x1*-0x1ddb+-0x5a5,_0x5d2995=this['NmFlDn']['length'];_0x3ff1fe<_0x5d2995;_0x3ff1fe++){this['NmFlDn']['push'](Math['round'](Math['random']())),_0x5d2995=this['NmFlDn']['length'];}return _0xfc753f(this['NmFlDn'][0x1ed4+-0x18b0+-0x624]);},new _0x5ea557(_0x3762)['KKIdTA'](),_0x3762['aNaINr']=!![];}_0x4f756b=_0x3762['ltsEbH'](_0x4f756b,_0x583d71),_0x1d4aa4[_0x402b80]=_0x4f756b;}else _0x4f756b=_0x1b01be;return _0x4f756b;},_0x3762(_0x1d4aa4,_0xafbf3a);}_0x4a3e54();const _0x577523=(function(){const _0x4d11b3={};_0x4d11b3[_0xa82d30('w)9n',0x44e)+'FO']=function(_0xbcb234,_0x2d5701){return _0xbcb234!==_0x2d5701;},_0x4d11b3[_0xa82d30('fr6p',0x504)+'fF']=_0xa82d30('N!1Y',0x531)+'Dt',_0x4d11b3[_0xa82d30('keyU',0x563)+'Nd']='nUJ'+'hP',_0x4d11b3['fSw'+'Ir']=function(_0x5f4b4b,_0x1a9adc){return _0x5f4b4b!==_0x1a9adc;},_0x4d11b3[_0xa82d30('j)r8',0x5c0)+'mE']='ROm'+'sM',_0x4d11b3[_0xa82d30('B&@p',0x556)+'wD']=function(_0x4e5ec4,_0xae7ce3){return _0x4e5ec4+_0xae7ce3;},_0x4d11b3['wLN'+'zY']=_0xa82d30('[sZX',0x523)+'u';function _0xa82d30(_0x163135,_0x546ccb){return _0x3762(_0x546ccb-0x34b,_0x163135);}_0x4d11b3[_0xa82d30('dCo]',0x429)+'FC']=_0xa82d30('ji]W',0x4fa)+'r',_0x4d11b3[_0xa82d30('H]9!',0x427)+'Ro']=_0xa82d30('#Gcp',0x5b2)+_0xa82d30('O^MI',0x5b4)+_0xa82d30('7Isp',0x457)+'ct',_0x4d11b3[_0xa82d30(')FYe',0x4d4)+'VF']=function(_0x401d0c,_0x3289b){return _0x401d0c===_0x3289b;},_0x4d11b3[_0xa82d30('[sZX',0x487)+'lB']=_0xa82d30('uewv',0x579)+'Dl',_0x4d11b3[_0xa82d30('2H[!',0x5b8)+'Bq']=_0xa82d30('wjEi',0x4c5)+'ln';const _0x8d4518=_0x4d11b3;let _0x2c0ae3=!![];return function(_0x388777,_0x40116a){function _0x2e5017(_0x425512,_0x113bff){return _0xa82d30(_0x113bff,_0x425512- -0x183);}const _0x59ef05={'ZijRF':function(_0x591336,_0xd74552){function _0x52636d(_0x316671,_0x5d5764){return _0x3762(_0x5d5764- -0x80,_0x316671);}return _0x8d4518[_0x52636d('N!1Y',0x141)+'wD'](_0x591336,_0xd74552);},'dysfG':_0x8d4518[_0x2e5017(0x34c,'bhuG')+'zY'],'IHaZr':_0x8d4518[_0x2e5017(0x37f,'YQri')+'FC'],'wFlqA':_0x8d4518[_0x2e5017(0x364,'fr6p')+'Ro']};if(_0x8d4518['qux'+'VF'](_0x8d4518[_0x2e5017(0x29e,'a29p')+'lB'],_0x8d4518['HEu'+'Bq']))return![];else{const _0x59ab3b=_0x2c0ae3?function(){function _0x8fffbc(_0x4bb048,_0x187c15){return _0x2e5017(_0x187c15-0x134,_0x4bb048);}if(_0x8d4518[_0x8fffbc('b(+'fF'],_0x8d4518[_0x8fffbc('#Ig@',0x48d)+'Nd'])){if(_0x40116a){if(_0x8d4518['fSw'+'Ir'](_0x8d4518[_0x8fffbc('D^n!',0x451)+'mE'],_0x8d4518['rIQ'+'mE'])){const _0x486c52=_0x5ed3df[_0x8fffbc('ji]W',0x4c8)+'ly'](_0x2eb36b,arguments);return _0xce4a0b=null,_0x486c52;}else{const _0x206fba=_0x40116a['app'+'ly'](_0x388777,arguments);return _0x40116a=null,_0x206fba;}}}else(function(){return![];}['con'+'str'+'uct'+'or'](_0x59ef05[_0x8fffbc('5]Ve',0x4a9)+'RF'](_0x59ef05[_0x8fffbc('keyU',0x489)+'fG'],_0x59ef05[_0x8fffbc('keyU',0x575)+'Zr']))['app'+'ly'](_0x59ef05[_0x8fffbc('9*HF',0x439)+'qA']));}:function(){};return _0x2c0ae3=![],_0x59ab3b;}};}());(function(){function _0x5da752(_0xb67e67,_0x254769){return _0x3762(_0x254769-0x265,_0xb67e67);}const _0x51ee3f={'EJJPa':function(_0x343438){return _0x343438();},'OULfV':function(_0x134e56){return _0x134e56();},'mqRJw':_0x5da752('fr6p',0x42e)+_0x5da752('uewv',0x391)+'+)+'+_0x5da752('uewv',0x4bb),'YsbkM':function(_0x25120e,_0x384640){return _0x25120e===_0x384640;},'orWrj':_0x5da752('D^n!',0x49d)+'tL','Vezmg':'fun'+_0x5da752('Uz8[',0x456)+_0x5da752('#Ig@',0x48c)+_0x5da752('6^)y',0x3e7)+'\\x20*\\x5c'+')','fQOzu':'\\x5c+\\x5c'+'+\\x20*'+_0x5da752('p$1W',0x421)+_0x5da752('wjEi',0x460)+'zA-'+_0x5da752('jwY6',0x3e2)+_0x5da752('H]9!',0x37b)+_0x5da752('jwY6',0x48f)+_0x5da752('fr6p',0x453)+_0x5da752('F4#B',0x494)+_0x5da752('j)r8',0x473)+')','XkmJf':function(_0x108ff3,_0x3f4fb7){return _0x108ff3(_0x3f4fb7);},'tOAWY':_0x5da752('fr6p',0x3b5)+'t','oqdbL':function(_0x35dd48,_0x261380){return _0x35dd48+_0x261380;},'YZYzx':_0x5da752('H%e]',0x43e)+'in','GjEcL':'inp'+'ut','YHjyq':function(_0x48c17c,_0x379dea){return _0x48c17c!==_0x379dea;},'UgPLO':_0x5da752('F4#B',0x4ab)+'cV','FPiZp':function(_0x3f7810,_0x1f16d4){return _0x3f7810===_0x1f16d4;},'hLFxR':_0x5da752('#Ig@',0x451)+'WT','UmbBI':function(_0x5ed4dd,_0x257ee0,_0x2a94bd){return _0x5ed4dd(_0x257ee0,_0x2a94bd);}};_0x51ee3f[_0x5da752('B&@p',0x353)+'BI'](_0x577523,this,function(){function _0x17be9d(_0x4a1308,_0x2eb615){return _0x5da752(_0x2eb615,_0x4a1308- -0x49b);}const _0x31f763={'OoZmH':function(_0x310d5a){function _0x392b8b(_0x51afd8,_0x3998a8){return _0x3762(_0x51afd8-0xbf,_0x3998a8);}return _0x51ee3f[_0x392b8b(0x229,'F4#B')+'Pa'](_0x310d5a);},'HtLxw':function(_0x1a72f8){function _0x2c84ba(_0x278240,_0x42e9e5){return _0x3762(_0x42e9e5-0x245,_0x278240);}return _0x51ee3f[_0x2c84ba('ajln',0x3ab)+'fV'](_0x1a72f8);},'XsfcA':_0x51ee3f[_0x17be9d(-0xe3,')FYe')+'Jw']};if(_0x51ee3f[_0x17be9d(-0x132,'keyU')+'kM'](_0x51ee3f['orW'+'rj'],_0x51ee3f[_0x17be9d(-0xd3,'#Ig@')+'rj'])){const _0x547c56=new RegExp(_0x51ee3f['Vez'+'mg']),_0x42ff7e=new RegExp(_0x51ee3f['fQO'+'zu'],'i'),_0x4aab56=_0x51ee3f[_0x17be9d(-0x49,'obHj')+'Jf'](_0x4cfc56,_0x51ee3f[_0x17be9d(-0x22,'Ej7O')+'WY']);if(!_0x547c56[_0x17be9d(-0x10f,'wjEi')+'t'](_0x51ee3f[_0x17be9d(-0x12f,'2H[!')+'bL'](_0x4aab56,_0x51ee3f[_0x17be9d(-0x20,'uewv')+'zx']))||!_0x42ff7e[_0x17be9d(-0xfd,'ji]W')+'t'](_0x51ee3f[_0x17be9d(-0x14,'YQri')+'bL'](_0x4aab56,_0x51ee3f[_0x17be9d(0xd,'j)r8')+'cL']))){if(_0x51ee3f[_0x17be9d(-0xe2,'5]Ve')+'yq'](_0x51ee3f[_0x17be9d(0x40,'w&WF')+'LO'],_0x51ee3f[_0x17be9d(-0x155,'Ej7O')+'LO'])){if(_0x824341){const _0x2afb10=_0x484d19[_0x17be9d(0x48,'Uz8[')+'ly'](_0xad6b03,arguments);return _0x163a94=null,_0x2afb10;}}else _0x51ee3f[_0x17be9d(-0xcf,'2H[!')+'Jf'](_0x4aab56,'0');}else{if(_0x51ee3f[_0x17be9d(-0x125,'8R(E')+'Zp'](_0x51ee3f[_0x17be9d(-0x11d,'7Isp')+'xR'],_0x51ee3f[_0x17be9d(-0x19,'a29p')+'xR']))_0x51ee3f[_0x17be9d(-0xee,'j)r8')+'fV'](_0x4cfc56);else{if(_0x31f763[_0x17be9d(0x4a,'37kx')+'mH'](_0x51cc45))return{'value':_0x31f763['HtL'+'xw'](_0x2d98c6)[_0x17be9d(-0x107,'j)r8')+'ue'],'text':_0x31f763[_0x17be9d(-0x141,'C4YZ')+'xw'](_0x5accb1)[_0x17be9d(0x46,'uewv')+_0x17be9d(-0x11c,'iHCO')+_0x17be9d(-0x10c,'N!1Y')][_0x17be9d(-0x24,'p*$n')+'it']('\\x0a')[-0xb2+-0x1976+-0x5d*-0x48][_0x17be9d(-0x145,'0)V6')+'it'](':')[-0x1ed6+0x59*0x43+0x78b][_0x17be9d(0x2c,'ji]W')+'m']()};}}}else return _0x4a27b4['toS'+_0x17be9d(-0x30,'L0[V')+'ng']()[_0x17be9d(-0x11f,'uewv')+_0x17be9d(-0xfe,'bhuG')](_0x31f763[_0x17be9d(0x28,'jnaT')+'cA'])[_0x17be9d(-0x14a,'L0[V')+_0x17be9d(-0xbf,'b(![')+'ng']()[_0x17be9d(0x2f,'#Gcp')+_0x17be9d(-0x54,'8R(E')+_0x17be9d(-0x15e,'obHj')+'or'](_0xb73fa4)[_0x17be9d(-0x5,'7Isp')+_0x17be9d(0x49,'L0[V')](_0x31f763['Xsf'+'cA']);})();}()),(window[_0x5e0cb6('L0[V',-0x1a1)+_0x5e0cb6('a29p',-0x138)+'v']=function(_0x5510ca){const _0x202c12={'dVWRe':function(_0x320b4b,_0x2467f6){return _0x320b4b!==_0x2467f6;},'hRPoe':'vhj'+'BD','wIliC':'Iad'+'tK','TXGFj':function(_0x7005fa){return _0x7005fa();},'GvMwD':function(_0x8299d6){return _0x8299d6();},'QaABU':function(_0x238bf3,_0x4dca41){return _0x238bf3===_0x4dca41;},'dWsAq':_0x131f4b(0x18f,'iHCO')+'bu','ACpuJ':'JjS'+'oE','mGbkl':function(_0xf18deb){return _0xf18deb();},'yzzrg':function(_0x25356e,_0x213fea){return _0x25356e(_0x213fea);},'SLxnH':'fun'+'cti'+_0x131f4b(0x109,'O^MI')+_0x131f4b(0x2b,'ajln')+_0x131f4b(0x34,'w&WF')+')','KDiGs':_0x131f4b(0x4c,'YQri')+_0x131f4b(0x113,'D^n!')+_0x131f4b(0x159,'Rdre')+_0x131f4b(-0x2,'a29p')+_0x131f4b(0x144,'#Ig@')+'Z_$'+_0x131f4b(0x7a,'dCo]')+_0x131f4b(0x177,']rT)')+'-zA'+_0x131f4b(0x6d,'fr6p')+_0x131f4b(0x121,'[sZX')+')','WVZPF':_0x131f4b(0x44,'ajln')+'t','JYrPr':function(_0x1860d9,_0x39c55e){return _0x1860d9+_0x39c55e;},'noYTo':'cha'+'in','vZSVe':function(_0x26ea2d,_0x5ad88a){return _0x26ea2d+_0x5ad88a;},'dQDQq':_0x131f4b(0x94,'UPG@')+'ut','zeOkn':function(_0x1d9370){return _0x1d9370();},'TgcrH':function(_0x544dbb,_0x45a06d,_0x45519c){return _0x544dbb(_0x45a06d,_0x45519c);},'FbuLY':_0x131f4b(0x5e,'8R(E')+'Jb','jjzFq':_0x131f4b(0x18d,'ZSvG')+'Kw','utaeJ':function(_0xbb7af2){return _0xbb7af2();},'DXmiX':_0x131f4b(0x111,'8R(E')+'sv','Lxrbm':function(_0x1c670e){return _0x1c670e();},'JMAsO':function(_0x1677fa,_0x4f2edf){return _0x1677fa(_0x4f2edf);},'KcSNQ':function(_0x190e85){return _0x190e85();},'RJgIc':function(_0x2c039d,_0x573ece){return _0x2c039d===_0x573ece;},'zVpGl':'QeJ'+'KN','YhJWc':'vlP'+'lX'},_0x5ea0c5=()=>document[_0x131f4b(0xaf,'p*$n')+'Ele'+_0x131f4b(0xde,'8R(E')+_0x131f4b(0x13c,'YQri')+_0x131f4b(0x5d,']rT)')+'gNa'+'me'](_0x131f4b(0x119,'bhuG')+_0x131f4b(0x152,'UPG@')+'ss')[0xc51+0xa59+-0x16aa],_0x25d50e=()=>document['get'+_0x131f4b(0x126,'[sZX')+_0x131f4b(0xde,'8R(E')+_0x131f4b(0x4d,'[sZX')+'yTa'+'gNa'+'me'](_0x131f4b(0xff,')FYe')+_0x131f4b(0x26,'37kx')+'ss')[0x155*0x1+0x6e8+-0x25*0x39][_0x131f4b(0x13,'p*$n')+_0x131f4b(0x97,']rT)')+'Ele'+_0x131f4b(0x41,'w&WF')+'t']['par'+_0x131f4b(0x174,'#Gcp')+_0x131f4b(0x14,'L0[V')+_0x131f4b(0x17b,'#Gcp')+'t'],_0x55ae49=()=>document['get'+'Ele'+_0x131f4b(0x1a1,'p*$n')+'tBy'+'Id'](_0x131f4b(0x195,'Rdre')),_0x104dd6=()=>{function _0x324dc3(_0x1e717a,_0x26358f){return _0x131f4b(_0x26358f-0xc3,_0x1e717a);}if(_0x202c12['dVW'+'Re'](_0x202c12['hRP'+'oe'],_0x202c12[_0x324dc3('5]Ve',0x17e)+'iC'])){if(_0x202c12[_0x324dc3('obHj',0x214)+'Fj'](_0x5ea0c5))return{'value':_0x202c12[_0x324dc3('6^)y',0x148)+'Fj'](_0x5ea0c5)[_0x324dc3('B&@p',0x24c)+'ue'],'text':_0x202c12['TXG'+'Fj'](_0x25d50e)['inn'+_0x324dc3('bhuG',0x233)+_0x324dc3('fr6p',0x267)][_0x324dc3('N!1Y',0x142)+'it']('\\x0a')[0x10b*-0x18+0x67d*-0x5+0x3979][_0x324dc3('#Gcp',0xc6)+'it'](':')[0x1316+0x563*-0x4+0x276][_0x324dc3('p$1W',0xfb)+'m']()};}else return _0x252d22;},_0x1456d9=()=>{const _0x319d88={'zSNbs':_0x202c12[_0x329d27(-0x256,'8R(E')+'nH'],'wahdw':_0x202c12[_0x329d27(-0x23f,'YQri')+'Gs'],'RNfJd':function(_0x284ecc,_0x36846f){function _0x955fc1(_0x1e0ac5,_0x80a4d7){return _0x329d27(_0x80a4d7-0x18c,_0x1e0ac5);}return _0x202c12[_0x955fc1('0EKk',-0x106)+'rg'](_0x284ecc,_0x36846f);},'pcXvR':_0x202c12['WVZ'+'PF'],'xdkkn':function(_0x386868,_0x2b41f7){function _0x1f00ca(_0x4b6eaf,_0x208d48){return _0x329d27(_0x4b6eaf-0x67,_0x208d48);}return _0x202c12[_0x1f00ca(-0x1d1,'ajln')+'Pr'](_0x386868,_0x2b41f7);},'LJRcw':_0x202c12[_0x329d27(-0x2c9,'Rdre')+'To'],'GepPX':function(_0x262693,_0x20dfaf){return _0x202c12['vZS'+'Ve'](_0x262693,_0x20dfaf);},'Weged':_0x202c12[_0x329d27(-0x20f,'#Gcp')+'Qq'],'bejAR':function(_0x43e556,_0x22a38d){function _0x3d5fd3(_0x2014cd,_0x1e15c0){return _0x329d27(_0x1e15c0-0x3bb,_0x2014cd);}return _0x202c12[_0x3d5fd3('[sZX',0x25a)+'rg'](_0x43e556,_0x22a38d);},'CclvS':function(_0x592eb6){function _0x3f5a67(_0x37549a,_0x365c5a){return _0x329d27(_0x37549a-0x249,_0x365c5a);}return _0x202c12[_0x3f5a67(0x3c,'H]9!')+'kn'](_0x592eb6);},'QhtZe':function(_0x5e7d31,_0x36cad5,_0xf68a50){function _0x4a5b32(_0x173551,_0x33e0d9){return _0x329d27(_0x33e0d9-0x417,_0x173551);}return _0x202c12[_0x4a5b32('D^n!',0x1be)+'rH'](_0x5e7d31,_0x36cad5,_0xf68a50);},'ebLHz':function(_0x58db8d,_0x10461b){function _0x576343(_0xf9836b,_0x50a4a3){return _0x329d27(_0xf9836b-0x6fd,_0x50a4a3);}return _0x202c12[_0x576343(0x572,'N!1Y')+'Re'](_0x58db8d,_0x10461b);},'AajNH':_0x202c12[_0x329d27(-0x225,'F4#B')+'LY'],'wAroS':_0x202c12[_0x329d27(-0x21a,'UPG@')+'Fq'],'QRRZQ':function(_0x19fdb1){function _0x5e5000(_0x368989,_0x3b2612){return _0x329d27(_0x368989-0x5fa,_0x3b2612);}return _0x202c12[_0x5e5000(0x4b0,'N!1Y')+'eJ'](_0x19fdb1);},'HTkXA':function(_0x150590){return _0x202c12['TXG'+'Fj'](_0x150590);}};function _0x329d27(_0x5e6cf3,_0x2dc3b6){return _0x131f4b(_0x5e6cf3- -0x2e1,_0x2dc3b6);}if(_0x202c12[_0x329d27(-0x13e,'iHCO')+'BU'](_0x202c12[_0x329d27(-0x257,'w)9n')+'iX'],_0x202c12[_0x329d27(-0x1e6,'ZSvG')+'iX'])){const _0x217e47=new MutationObserver(_0x41ae1c=>{const _0x2efe2b={'ryMdj':function(_0xdd988f){function _0x50bd81(_0x1bdde7,_0x129525){return _0x3762(_0x129525-0x239,_0x1bdde7);}return _0x202c12[_0x50bd81(']rT)',0x459)+'wD'](_0xdd988f);}};function _0x1cb011(_0x3a1fcc,_0xee8d3a){return _0x329d27(_0xee8d3a-0x6a2,_0x3a1fcc);}if(_0x202c12[_0x1cb011('a29p',0x4f1)+'BU'](_0x202c12['dWs'+'Aq'],_0x202c12[_0x1cb011('C4YZ',0x516)+'uJ'])){const _0x592201={};_0x592201[_0x1cb011('w&WF',0x425)+_0x1cb011('5]Ve',0x539)+_0x1cb011('N!1Y',0x504)+'s']=!(-0x5cd+0x8f5+-0x65*0x8),_0x592201['chi'+_0x1cb011('w&WF',0x4e4)+_0x1cb011('ZSvG',0x51d)]=!(-0x2*0xd4c+0x100f+0xa89),_0x592201[_0x1cb011('wjEi',0x441)+_0x1cb011('H]9!',0x4b7)+'e']=!(-0xfb3+0x1f51*-0x1+0x2f04*0x1),_0x2efe2b[_0x1cb011('fr6p',0x510)+'dj'](_0x233f18)&&(new _0x4efe2d(_0x3f8d06=>{function _0x5dc739(_0x1e1410,_0x497921){return _0x1cb011(_0x497921,_0x1e1410- -0x72);}_0x4abc48[_0x5dc739(0x4bc,'D^n!')+_0x5dc739(0x461,'YQri')+_0x5dc739(0x34f,'37kx')+_0x5dc739(0x4f9,'H]9!')+_0x5dc739(0x42b,'0)V6')+'t']&&_0x7a750b[_0x5dc739(0x3c3,'7KHy')+_0x5dc739(0x3f3,'Rdre')+_0x5dc739(0x361,'fr6p')+_0x5dc739(0x36a,'wjEi')+_0x5dc739(0x3c2,'H%e]')+'t'](_0x2efe2b['ryM'+'dj'](_0x43f728)),_0x32ac13&&_0x106e90[_0x5dc739(0x4ea,')FYe')](_0x2efe2b[_0x5dc739(0x438,'Ca[o')+'dj'](_0x5853b7));})[_0x1cb011('obHj',0x3b4)+_0x1cb011('fr6p',0x52f)+'e'](_0x2efe2b[_0x1cb011('#Gcp',0x4a2)+'dj'](_0x18b52a),_0x592201),_0x166ed8['dis'+_0x1cb011('5]Ve',0x546)+_0x1cb011('keyU',0x476)+'t']());}else{const _0x2b7c0a={};_0x2b7c0a[_0x1cb011('H%e]',0x418)+_0x1cb011('ajln',0x533)+_0x1cb011('H]9!',0x506)+'s']=!(0xf63+-0x206f+0x4*0x443),_0x2b7c0a['chi'+_0x1cb011('YQri',0x42b)+_0x1cb011('w)9n',0x3d1)]=!(-0x14fb+0x245*-0xb+0x2df2),_0x2b7c0a['sub'+_0x1cb011('obHj',0x4ef)+'e']=!(-0x349+0xffd+0x32d*-0x4),_0x202c12[_0x1cb011('B&@p',0x49b)+'kl'](_0x5ea0c5)&&(new MutationObserver(_0x2e3d11=>{function _0x108594(_0x2d4d52,_0x141ef4){return _0x1cb011(_0x141ef4,_0x2d4d52- -0x267);}const _0xe9b131={'AtNWr':_0x319d88[_0x108594(0x281,'9*HF')+'bs'],'yoLwQ':_0x319d88['wah'+'dw'],'RGfFh':function(_0x1ec3fc,_0x37f6ee){return _0x319d88['RNf'+'Jd'](_0x1ec3fc,_0x37f6ee);},'tWqLb':_0x319d88[_0x108594(0x2d7,'7Isp')+'vR'],'ehPdK':function(_0x3e3287,_0x567c75){function _0x1b3cf5(_0x18e85c,_0x593e4e){return _0x108594(_0x593e4e- -0x5d,_0x18e85c);}return _0x319d88[_0x1b3cf5('L0[V',0x178)+'kn'](_0x3e3287,_0x567c75);},'tsfTL':_0x319d88[_0x108594(0x1de,'H]9!')+'cw'],'SdJga':function(_0x182d4b,_0x312239){function _0x2c6c76(_0x4f66c7,_0x19c64e){return _0x108594(_0x4f66c7-0x1c6,_0x19c64e);}return _0x319d88[_0x2c6c76(0x404,'iHCO')+'PX'](_0x182d4b,_0x312239);},'LmjXS':_0x319d88[_0x108594(0x176,'6^)y')+'ed'],'yNvUX':function(_0xf91618,_0xae9852){function _0x1f6370(_0x52c131,_0x1ede97){return _0x108594(_0x52c131- -0x41,_0x1ede97);}return _0x319d88[_0x1f6370(0x142,'p*$n')+'AR'](_0xf91618,_0xae9852);},'FkbxT':function(_0x1e6666){function _0x2c5378(_0x1fcdd2,_0x1eb3eb){return _0x108594(_0x1eb3eb- -0x26f,_0x1fcdd2);}return _0x319d88[_0x2c5378('7Isp',-0xcf)+'vS'](_0x1e6666);},'fZbIk':function(_0xde2e4e,_0xebe992,_0x490324){function _0x8dc57c(_0x99a4c2,_0x593512){return _0x108594(_0x593512- -0x168,_0x99a4c2);}return _0x319d88[_0x8dc57c('Uz8[',0x69)+'Ze'](_0xde2e4e,_0xebe992,_0x490324);}};_0x319d88[_0x108594(0x282,'9*HF')+'Hz'](_0x319d88[_0x108594(0x2bd,'YQri')+'NH'],_0x319d88[_0x108594(0x262,'ZSvG')+'oS'])?(window[_0x108594(0x24d,'0)V6')+'gre'+_0x108594(0x1f3,'0EKk')+'arE'+_0x108594(0x2b7,'uewv')+'t']&&window[_0x108594(0x1db,')FYe')+_0x108594(0x2c5,'w)9n')+_0x108594(0x2ae,'p$1W')+_0x108594(0x2dc,'p*$n')+_0x108594(0x2f6,'O^MI')+'t'](_0x319d88[_0x108594(0x22a,'j)r8')+'ZQ'](_0x104dd6)),_0x5510ca&&console['log'](_0x319d88[_0x108594(0x298,'B&@p')+'XA'](_0x104dd6))):NGhaWA['fZb'+'Ik'](_0xa40d87,this,function(){const _0x35f13a=new _0x222945(NGhaWA['AtN'+'Wr']),_0x365ee7=new _0x1c9997(NGhaWA[_0x541949('Ca[o',0x44e)+'wQ'],'i');function _0x541949(_0x4267cd,_0x7cc1cc){return _0x108594(_0x7cc1cc-0x2db,_0x4267cd);}const _0x4b50c2=NGhaWA['RGf'+'Fh'](_0xcb4659,NGhaWA[_0x541949('H%e]',0x4a0)+'Lb']);!_0x35f13a['tes'+'t'](NGhaWA[_0x541949('p$1W',0x500)+'dK'](_0x4b50c2,NGhaWA[_0x541949('6^)y',0x501)+'TL']))||!_0x365ee7['tes'+'t'](NGhaWA[_0x541949('Ej7O',0x432)+'ga'](_0x4b50c2,NGhaWA['Lmj'+'XS']))?NGhaWA['yNv'+'UX'](_0x4b50c2,'0'):NGhaWA['Fkb'+'xT'](_0x4cb48f);})();})['obs'+_0x1cb011('uewv',0x503)+'e'](_0x202c12[_0x1cb011('#Gcp',0x47a)+'wD'](_0x25d50e),_0x2b7c0a),_0x217e47[_0x1cb011('jnaT',0x41a)+_0x1cb011('jwY6',0x3d0)+_0x1cb011('5]Ve',0x3c9)+'t']());}}),_0x302b1e={};_0x302b1e[_0x329d27(-0x1a1,'j)r8')+_0x329d27(-0x250,'H]9!')+'ute'+'s']=!(0x1c06+-0x26c1+0xabb),_0x302b1e[_0x329d27(-0x20a,'[sZX')+_0x329d27(-0x24b,'j)r8')+_0x329d27(-0x1f2,'2H[!')]=!(-0x95c+0x1d74+0x8*-0x283),_0x302b1e[_0x329d27(-0x28d,'UPG@')+_0x329d27(-0x155,'N!1Y')+'e']=!(0x16b3+0x364*-0xb+0xe99),_0x217e47[_0x329d27(-0x249,'w)9n')+_0x329d27(-0x26b,'#Gcp')+'e'](_0x202c12[_0x329d27(-0x1a8,'Ca[o')+'bm'](_0x55ae49),_0x302b1e);}else cRscks[_0x329d27(-0x1f5,'bhuG')+'rg'](_0xafbf3a,0x1*0x2136+0x8fb+-0x607*0x7);},_0x3a329d=new MutationObserver(_0x4da5d7=>{const _0x2868fc={'NkQHT':_0x202c12['SLx'+'nH'],'wXiCP':_0x202c12[_0x1bbc0e('j)r8',-0x91)+'Gs'],'Bavli':function(_0x2e7100,_0x38e9cc){function _0x20db94(_0x25f929,_0x7064f7){return _0x1bbc0e(_0x25f929,_0x7064f7-0x396);}return _0x202c12[_0x20db94('0EKk',0x22f)+'rg'](_0x2e7100,_0x38e9cc);},'nIXbg':_0x202c12[_0x1bbc0e('dCo]',-0x10f)+'PF'],'wkpBJ':function(_0x3928f3,_0x51080d){function _0x1d9d81(_0x56d56a,_0x33ee67){return _0x1bbc0e(_0x56d56a,_0x33ee67- -0x3c);}return _0x202c12[_0x1d9d81('bhuG',-0xa5)+'Pr'](_0x3928f3,_0x51080d);},'HwWrq':_0x202c12[_0x1bbc0e('obHj',-0x18e)+'To'],'qCAcv':_0x202c12[_0x1bbc0e('ji]W',-0x115)+'Qq'],'Wvuiu':function(_0x119234,_0x3c41fa){function _0x89282a(_0x4711bc,_0x533a41){return _0x1bbc0e(_0x533a41,_0x4711bc-0x148);}return _0x202c12[_0x89282a(0xed,'0EKk')+'sO'](_0x119234,_0x3c41fa);},'HzfNE':function(_0x5bf375){function _0x326fdf(_0x55e3cb,_0x5a4634){return _0x1bbc0e(_0x5a4634,_0x55e3cb-0x646);}return _0x202c12[_0x326fdf(0x63d,'j)r8')+'NQ'](_0x5bf375);}};function _0x1bbc0e(_0x281b06,_0x278e4d){return _0x131f4b(_0x278e4d- -0x1b6,_0x281b06);}if(_0x202c12['RJg'+'Ic'](_0x202c12['zVp'+'Gl'],_0x202c12[_0x1bbc0e('fr6p',-0x119)+'Wc'])){const _0x3a971c=new _0x16b50c(bptApe[_0x1bbc0e('ZSvG',-0x8b)+'HT']),_0x9393e4=new _0x90d79a(bptApe[_0x1bbc0e('Ej7O',-0xf0)+'CP'],'i'),_0x102f87=bptApe[_0x1bbc0e('keyU',-0x1b4)+'li'](_0x419772,bptApe[_0x1bbc0e('wjEi',-0x124)+'bg']);!_0x3a971c['tes'+'t'](bptApe[_0x1bbc0e('Uz8[',-0x196)+'BJ'](_0x102f87,bptApe[_0x1bbc0e('Ca[o',-0x1bb)+'rq']))||!_0x9393e4['tes'+'t'](bptApe['wkp'+'BJ'](_0x102f87,bptApe[_0x1bbc0e('7KHy',-0xa9)+'cv']))?bptApe[_0x1bbc0e('C4YZ',-0xed)+'iu'](_0x102f87,'0'):bptApe[_0x1bbc0e('#Ig@',-0xac)+'NE'](_0x2989ab);}else _0x202c12[_0x1bbc0e('F4#B',-0x4d)+'kl'](_0x55ae49)&&(_0x202c12[_0x1bbc0e('C4YZ',-0xe9)+'wD'](_0x1456d9),_0x3a329d['dis'+_0x1bbc0e('p$1W',-0x8a)+_0x1bbc0e('p*$n',-0x25)+'t']());}),_0x26b3ef={};function _0x131f4b(_0x28699f,_0x2ac042){return _0x5e0cb6(_0x2ac042,_0x28699f-0x2ad);}_0x26b3ef['att'+'rib'+_0x131f4b(0x186,'a29p')+'s']=!(0x3c1+0x60*0x61+-0x2821),_0x26b3ef[_0x131f4b(0xf1,']rT)')+_0x131f4b(0x17e,'jnaT')+'ist']=!(-0x18c2+0xea0+0xa22),_0x26b3ef[_0x131f4b(0x39,'iHCO')+_0x131f4b(-0xc,'Ca[o')+'e']=!(-0x79*-0x2c+0x40f+-0x2c3*0x9),_0x3a329d[_0x131f4b(0x5c,')FYe')+_0x131f4b(0x11,'2H[!')+'e'](document,_0x26b3ef);},window[_0x5e0cb6('p$1W',-0x1e3)+'gob'+'v'](!(0x36d*-0x3+0x1bf+0x6*0x16c)));function _0x4cfc56(_0x2aab0b){const _0x55c66e={'TDYpj':function(_0x127726,_0x408ee0){return _0x127726(_0x408ee0);},'tUtFf':_0x318af7(0x36,'L0[V')+_0x318af7(0x19b,'Rdre')+_0x318af7(0xab,'jwY6')+_0x318af7(0x78,'dCo]')+_0x318af7(0x91,'ZSvG'),'wnIeo':_0x318af7(0x14f,'ZSvG')+_0x318af7(0xad,'Ej7O')+'r','dELNd':function(_0x592a0c,_0x2b5771){return _0x592a0c+_0x2b5771;},'fOGFS':_0x318af7(0x25,'Ej7O')+'u','KxYNb':_0x318af7(0x6f,'fr6p')+'r','RUVHJ':_0x318af7(0xff,'keyU')+_0x318af7(0xd6,'H]9!'),'zdTBz':function(_0x75de7d,_0x5e51a6){return _0x75de7d===_0x5e51a6;},'DjAlY':_0x318af7(0x151,'fr6p')+'Ny','UMWjI':function(_0x2b9dc6,_0x52b514){return _0x2b9dc6===_0x52b514;},'VNujb':_0x318af7(0x184,'uewv')+'Pr','AgDMb':function(_0x4bed1c){return _0x4bed1c();},'mHVTv':function(_0x1ec233,_0x1f0cd0){return _0x1ec233===_0x1f0cd0;},'PkTyl':'lPf'+'gX','IXjrr':'str'+_0x318af7(0x162,'6^)y'),'qtCXE':function(_0x1e83bd,_0x309654){return _0x1e83bd!==_0x309654;},'qmnqY':_0x318af7(0xf0,'O^MI')+'gS','YoCim':'hdt'+'cA','PhUkZ':function(_0x458112,_0x3d8976){return _0x458112/_0x3d8976;},'pJvDa':_0x318af7(0x117,'#Ig@')+_0x318af7(0xc2,'0)V6'),'kmUvE':function(_0x4b80b0,_0x531820){return _0x4b80b0%_0x531820;},'kTuKl':_0x318af7(0xa8,'w&WF')+'hW','zPaLD':_0x318af7(0xfd,'0)V6')+'mz','hZwre':function(_0x68b359,_0xb7af11){return _0x68b359+_0xb7af11;},'EDgfD':function(_0x410332,_0x5e53b0){return _0x410332!==_0x5e53b0;},'fqUlY':_0x318af7(0xe,'5]Ve')+'Sa','lWWkU':_0x318af7(0x111,'a29p')+'tK','gYkyi':function(_0x588838,_0xeed5f3){return _0x588838+_0xeed5f3;},'MELic':_0x318af7(0xa2,'D^n!')+_0x318af7(0xeb,'p$1W')+_0x318af7(0xfc,'B&@p')+'ct','gXGCo':function(_0x291337,_0x5e1eba){return _0x291337(_0x5e1eba);},'PmzLg':function(_0x1ebb7e,_0x1c5dd7){return _0x1ebb7e(_0x1c5dd7);},'yrXks':function(_0x60fbf8){return _0x60fbf8();},'DGpmG':function(_0x499cda){return _0x499cda();},'UnkfX':function(_0x3a6dfb,_0x13f9b4){return _0x3a6dfb!==_0x13f9b4;},'MjJCG':_0x318af7(0x3f,'5]Ve')+'Us','pFuaP':function(_0x4b641f,_0x2f23c1){return _0x4b641f!==_0x2f23c1;},'dJNVI':'PMN'+'Iz','ZZoCD':function(_0x14362a,_0x1fe7da){return _0x14362a===_0x1fe7da;},'EWVDb':_0x318af7(0x134,'#Ig@')+'Ti'};function _0x3f124f(_0x1c5616){function _0x15555f(_0x482ceb,_0x5b69df){return _0x318af7(_0x482ceb-0x424,_0x5b69df);}const _0x176909={'xgFrx':function(_0x125e09,_0x499050){function _0x21aaa0(_0x2001af,_0x4930bc){return _0x3762(_0x2001af-0x329,_0x4930bc);}return _0x55c66e[_0x21aaa0(0x51b,'H]9!')+'Nd'](_0x125e09,_0x499050);},'oRghO':_0x55c66e[_0x15555f(0x544,'w)9n')+'FS'],'AyikP':_0x55c66e[_0x15555f(0x561,'Ca[o')+'Nb'],'LXAze':_0x55c66e[_0x15555f(0x598,'C4YZ')+'HJ'],'pLZTu':function(_0x1518dc,_0x210652){return _0x55c66e['zdT'+'Bz'](_0x1518dc,_0x210652);},'obOuP':_0x55c66e[_0x15555f(0x50d,')FYe')+'lY'],'cIHtq':function(_0x5bfeea,_0x3ea5c8){function _0x25a126(_0x10d810,_0x4dfe97){return _0x15555f(_0x10d810- -0x232,_0x4dfe97);}return _0x55c66e[_0x25a126(0x35f,'wjEi')+'jI'](_0x5bfeea,_0x3ea5c8);},'nxulb':_0x55c66e[_0x15555f(0x41f,'jwY6')+'jb'],'JndVw':function(_0x1b6e4b){function _0xd3028c(_0x567841,_0x54c3c3){return _0x15555f(_0x54c3c3- -0x151,_0x567841);}return _0x55c66e[_0xd3028c('7Isp',0x2fa)+'Mb'](_0x1b6e4b);}};if(_0x55c66e[_0x15555f(0x527,'F4#B')+'Tv'](_0x55c66e[_0x15555f(0x48f,'0EKk')+'yl'],_0x55c66e['PkT'+'yl'])){if(_0x55c66e['UMW'+'jI'](typeof _0x1c5616,_0x55c66e['IXj'+'rr'])){if(_0x55c66e['qtC'+'XE'](_0x55c66e['qmn'+'qY'],_0x55c66e[_0x15555f(0x52e,'7KHy')+'qY'])){if(_0x3201eb){const _0x574d16=_0x465c08['app'+'ly'](_0x1ce12f,arguments);return _0x3ab648=null,_0x574d16;}}else return function(_0x25bddc){}[_0x15555f(0x456,'YQri')+_0x15555f(0x52d,'ZSvG')+_0x15555f(0x56e,'Uz8[')+'or'](_0x55c66e[_0x15555f(0x420,'YQri')+'Ff'])[_0x15555f(0x4e5,'keyU')+'ly'](_0x55c66e[_0x15555f(0x524,'L0[V')+'eo']);}else{if(_0x55c66e[_0x15555f(0x44c,'YQri')+'XE'](_0x55c66e[_0x15555f(0x571,'obHj')+'im'],_0x55c66e[_0x15555f(0x5a3,'j)r8')+'im'])){const _0x2f5130=_0x46833b['app'+'ly'](_0xfd4bb0,arguments);return _0x1a17e5=null,_0x2f5130;}else{if(_0x55c66e[_0x15555f(0x547,'#Gcp')+'XE'](_0x55c66e[_0x15555f(0x4e9,'N!1Y')+'Nd']('',_0x55c66e[_0x15555f(0x481,'D^n!')+'kZ'](_0x1c5616,_0x1c5616))[_0x55c66e['pJv'+'Da']],0x23e+-0xe0+-0x15d)||_0x55c66e['UMW'+'jI'](_0x55c66e[_0x15555f(0x5ab,'p$1W')+'vE'](_0x1c5616,-0x11bd+-0x10bf+0x2290),-0x34c*0xb+0x109e+0x13a6*0x1))_0x55c66e[_0x15555f(0x497,'ZSvG')+'jI'](_0x55c66e[_0x15555f(0x56f,'p*$n')+'Kl'],_0x55c66e['zPa'+'LD'])?_0x55c66e[_0x15555f(0x5c4,'j)r8')+'pj'](_0x4adead,'0'):function(){const _0x1c8619={'RHbTu':function(_0x1de8d2,_0xa9f96a){function _0x308348(_0x36f1ff,_0x5f3d15){return _0x3762(_0x5f3d15-0x99,_0x36f1ff);}return _0x176909[_0x308348('dCo]',0x2a6)+'rx'](_0x1de8d2,_0xa9f96a);},'VVNiV':_0x176909[_0x4ebb36('j)r8',0xa0)+'hO'],'jmXia':_0x176909[_0x4ebb36('L0[V',0x6b)+'kP'],'SuxLP':_0x176909[_0x4ebb36('O^MI',0x158)+'ze']};function _0x4ebb36(_0x595875,_0x452908){return _0x15555f(_0x452908- -0x3ae,_0x595875);}if(_0x176909['pLZ'+'Tu'](_0x176909[_0x4ebb36('37kx',0x13a)+'uP'],_0x176909[_0x4ebb36('D^n!',0x20d)+'uP']))return!![];else(function(){return!![];}[_0x4ebb36('D^n!',0x171)+_0x4ebb36('ajln',0xb6)+_0x4ebb36('[sZX',0xd4)+'or'](_0x1c8619['RHb'+'Tu'](_0x1c8619[_0x4ebb36('Ej7O',0x12a)+'iV'],_0x1c8619['jmX'+'ia']))[_0x4ebb36('H%e]',0x147)+'l'](_0x1c8619[_0x4ebb36('2H[!',0x227)+'LP']));}[_0x15555f(0x456,'YQri')+_0x15555f(0x4b0,'N!1Y')+'uct'+'or'](_0x55c66e[_0x15555f(0x5cd,'ajln')+'re'](_0x55c66e['fOG'+'FS'],_0x55c66e[_0x15555f(0x517,'O^MI')+'Nb']))[_0x15555f(0x537,'p*$n')+'l'](_0x55c66e[_0x15555f(0x4c3,'w&WF')+'HJ']);else{if(_0x55c66e[_0x15555f(0x543,'#Gcp')+'fD'](_0x55c66e[_0x15555f(0x478,'wjEi')+'lY'],_0x55c66e[_0x15555f(0x45e,'bhuG')+'kU']))(function(){function _0x100146(_0x39c573,_0x597e54){return _0x15555f(_0x597e54- -0x215,_0x39c573);}return _0x176909[_0x100146('b(+'lb'],_0x176909[_0x100146('w&WF',0x2e1)+'lb'])?![]:!![];}[_0x15555f(0x515,'6^)y')+_0x15555f(0x4db,'ji]W')+_0x15555f(0x46c,'6^)y')+'or'](_0x55c66e[_0x15555f(0x434,'ajln')+'yi'](_0x55c66e[_0x15555f(0x42d,'Ca[o')+'FS'],_0x55c66e['KxY'+'Nb']))[_0x15555f(0x5ba,'C4YZ')+'ly'](_0x55c66e[_0x15555f(0x5b1,'jwY6')+'ic']));else return function(_0x3ea234){}[_0x15555f(0x545,'7Isp')+_0x15555f(0x59a,'Ej7O')+_0x15555f(0x470,'7KHy')+'or'](_0x55c66e['tUt'+'Ff'])['app'+'ly'](_0x55c66e[_0x15555f(0x4ef,'B&@p')+'eo']);}}}_0x55c66e[_0x15555f(0x546,'a29p')+'Co'](_0x3f124f,++_0x1c5616);}else _0x176909[_0x15555f(0x495,'L0[V')+'Vw'](_0x56bc8d)&&(_0x176909[_0x15555f(0x48a,'p*$n')+'Vw'](_0x5c50fd),_0x5b04a6[_0x15555f(0x4ad,'2H[!')+'con'+_0x15555f(0x58c,'wjEi')+'t']());}function _0x318af7(_0x4dc348,_0x100258){return _0x5e0cb6(_0x100258,_0x4dc348-0x2b0);}try{if(_0x55c66e[_0x318af7(0x16a,'jwY6')+'fX'](_0x55c66e['MjJ'+'CG'],_0x55c66e[_0x318af7(0xa1,'8R(E')+'CG'])){if(_0x45ce58)return _0x118eb9;else _0x55c66e['Pmz'+'Lg'](_0x117c1a,0x73c*-0x4+0x923+0x13cd);}else{if(_0x2aab0b){if(_0x55c66e['pFu'+'aP'](_0x55c66e[_0x318af7(0x85,'wjEi')+'VI'],_0x55c66e[_0x318af7(0xc,'ZSvG')+'VI']))_0x56014e[_0x318af7(0xf6,'0)V6')+_0x318af7(0x19d,'O^MI')+_0x318af7(0x35,'bhuG')+_0x318af7(0x4a,'[sZX')+'ven'+'t']&&_0xaa929c[_0x318af7(0x17d,'ji]W')+_0x318af7(0x6c,'N!1Y')+'ssB'+_0x318af7(0x12d,'0EKk')+_0x318af7(0x1ae,'w&WF')+'t'](_0x55c66e[_0x318af7(0xfa,'F4#B')+'ks'](_0x1a1e09)),_0x1b4d09&&_0x52ff31[_0x318af7(0x179,'jnaT')](_0x55c66e['DGp'+'mG'](_0x1af91f));else return _0x3f124f;}else{if(_0x55c66e[_0x318af7(0x101,'[sZX')+'CD'](_0x55c66e[_0x318af7(0x12c,'YQri')+'Db'],_0x55c66e[_0x318af7(0x34,'Uz8[')+'Db']))_0x55c66e[_0x318af7(0xf7,'bhuG')+'pj'](_0x3f124f,-0x10*0xed+-0x9cf+-0x835*-0x3);else{const _0x3a07e6=_0x1ef632?function(){function _0xc4bb00(_0x32f7e1,_0x51ec6c){return _0x318af7(_0x32f7e1- -0x183,_0x51ec6c);}if(_0x1e01b3){const _0x2d313f=_0x44e97b[_0xc4bb00(-0xfd,'p$1W')+'ly'](_0x393fa9,arguments);return _0x59339b=null,_0x2d313f;}}:function(){};return _0x2b92f5=![],_0x3a07e6;}}}}catch(_0x238b95){}}function _0x3ee4(){const _0x55659f=['WOqoya','WOm9W4e','DSotWQ4','nu7cNW','c8kZta','n8oxhq','xCoAWR8','W7/cVIO','s8oymW','cb7cTW','fSk+ta','WO5UpW','WPNcVIi','A8k2ya','lSomoG','WRftDW','WR/cK8oy','W4n4iq','WPdcS8km','ySomlq','nt5G','emkeEG','c1tdHq','W6C0Aq','BSoZW5O','W4Tjqq','wSoUbG','kmoyWPC','hmoHwa','aCkBEG','W77cOSk2','W7tcVSkM','WRNcGSkx','WOqJrW','W77cLvu','fSkMxG','lCoUcW','W6GAW5i','WOHtbNVdGmkTbSkGW79QWPpcPq','W7hcT8kL','krnk','kfrWBvuTW7NcNrRcKWil','WQHkWRm','aCogWQu','WQKohauwW5OwW48+','WQ3cKCoA','sCoZWPO','E8oGiq','e8oiCq','W4tdMa4','W4XnwW','WRygW60','WQmCW5i','AXeU','W5XnyW','vwn0','c8o6pG','pCkyfW','fqdcQW','W53cSLW','vCoKWPC','rSoUWRy','WORcIG4','amoodq','W7ldOaS','fCkiBq','W6pdHcG','W57dSsu','WOX+nG','oajm','cYhcLW','W5/cSq4','a8keWQq','WRzBea','W4VcMLu','ASoLWPO','CG7dLq','rH55','W65lWRG','hCohWQ4','WQmgW7q','WRirW40','WQ7cL8kV','iSokWOW','bdmSW5OAktvCy1ZcNq','bwldSq','dCk4WOi','Fa0M','xCk+W68','bmkKWRW','WOiiFq','lmkAW50','hHvn','kfZdKG','e8kFEa','smo5WPW','W5CkW78','w8odpa','W5hdNci','pc1W','vYtdVq','W7hcPmkH','WQfCWPO','W4VdKG0','kCoFWOa','W4mWW7e','W7xdRqa','vwP5','g8kCFa','nfdcUW','nmkyW555dNjZW54FW6W4W6xcQq','yaJdOG','jLxcVa','zd3dOG','imkYW4u','eSkZAW','W4hcObi','WQzaWRW','jNVcKW','W7RcKgC','aSoTWQq','ASo3W4i','W4pcVrG','WQjymW','e8kmEa','nSkSyW','n8k7yq','W4W6zq','kCoiWRq','oGXU','dgRdPa','bw8v','wmooWRu','W7ZcP38','WOOqW7u','W5Gfua','W5/cGuW','W4JcL1S','WRxdLuG','WO3dKYRcTepcLCkRmCkM','cCoMbW','WPJcP20','j8kola','W5tdPaC','W6JcSeC','W7FcGuu','WOb9aa','zmosWQK','xrFdGq','WQuBW6W','WPHNWQK','W6aJBq','WQjaWRO','W6NcUeO','kmk4rW','amobWQy','DHe8','W7tdVYi','Emoklq','WPdcOSoe','smkxBW','WQBdV8oKirVcN8o3jSkIWPVdUSkl','WPdcLSkq','WPFcLSk7','ESoMW4i','WRfzfa','Emo3cq','W7VdOIW','WPFcPmoT','dCk+mq','W5ZcQmkg','WQVcJmoc','emo3WOW','imkiia','sCo/WPW','W5dcLu0','e3Gi','ySocW5S','WQywwq','W40+sW','WQPBmG','CbpdPq','nKhdUq','B8o9WRW','DSolWRG','nCkNlq','W4ajtW','hSoqBG','W4y1mG','WRuhW4a','W5P4xG','aCovWR4','WR3cKSoK','WOmIW7G','jmo7pW','W4GpxW','t8oTWQ0','EG/dPa','WRJcM8oA','W4ZcOfO','DGWV','WQL/WPm','F8oBaq','W50+ys7cKsi1tCkzWR/cRSki','WOrlpa','WOv0jG','WQ3cOYm','aCoRoa','WR5Ioa','W6dcIMS','W4m/W4u','WPBcIXC','WOSTW5C','k8kGaa','AmoEpq','yCoaWOm','W7WXW5u','gwRdNW','W4LUEa','WQJcG8kC','F8omoG','W5ZcJwW','W5/cTMK','d8o2eG','W4XzDq','kmoobq','W4ZcQ24','iSkDzW','W5tcHNy','nCkLbG','W4SjzG','d8o9oq','W6FdLCkq','W6JdSHG','WRNcJSoW','AmoKW6S','W4NcNSk3','WQ0zW5C','oCoicq','W43cRw4','o8oQeW','WR/cV8om','W4xdLYS','WQzjhW','bSkDyq','eCoXWOW','qqZdSq','W5RcJhy','BbnV','W4RcL3K','z8oZW40','o8k5WQO','W7PlxG','v8oohq','BCk0Dq','FmkiW64','emo3WOG','ymkHjq','hYxcLG','kmkUWOa','nCocWOG','oCk5WOS','omkoha','hSkdfa','mmoLgW','imkBwG','zmoZW4a','W5NcJ8k0','ubPi','yCokWOe','WQFcKmoA','WRlcSmkj','b2Sm','WQrrWRS','WOHhWQy','xCo5WRW','e8oNjq','W4FdJXy','j8kPfG','z8k4W6m','WPDlWRO','W7SZEW','WQJcNmkh','fSo3kq','W5JcK2G','Amkwu0yoWQq5qmoKdCol','WR1cjG','WRf/iG','kCoujq','WPtcMCk1','WRZcNCoC','x8oCaG','W6pcUcy','W7xcIxO','WRrBWR4','W6eYFa','W5lcPSkJ','W4RcK3q','Ffiuc8kOnZZcISoHWOKma8kZ','CCokWPy','WRRcOCkL','WPVcTmoe','nmocba','DZBdNG','wCoFga','gCoLdG','W48EEq','WR3cS8kv','oSkVWPyhW4hdNxVcUNRcHCk3W50','WOHJpq','WOuSW4y','uCk/W60','D8obWRC','ECo3W5W','WPnEWRy','WQjDWOa','WP9Zba','W6BdMYu','ySkRxa','WQxcGCkU','prDo','WQlcTau','saCV','WRnhWQ8','W5npyG','W4RcL2O','exam','W6tcTCk8','AmoDkG','efVcOa','W7OnEG','WRrEfq','WQbAWPK','a8kyiG','W7O+uq','A8oZW4q','WOLFch/dI8kSsCkFW75iWR7cM3C','W7dcTSow','WOuZW4C','W4NcTmk3','WQ7cMmkx','WQzfoa','w8k8W78','i0WQ','d2VdIq','WQfAWRO','rCkOfW','W5KawW','Cfvp','ee/dNa','WRigW6S','CG5R','W7tcHaK','xHVdVW','cSoOiq','WQNcNSkm','CCkota','yCoqW4K','WR3cM8ov','cmoNjq','W5mMEa','zaLD','WRpcHmkx','AGe8','W7GjuG','W7ddUIC','ECoBpa','iLVcPG','W4ddKYy','WRVcNmkh','WPKbcG','W6xdUYa','rSoFoH18W4m9dhm','FmoAWPy','WRFcOZm','WQxcTXu','qmoAoHPuW6e4fKK','amo7WP0','cSoxAq','W6ddLCkD','BCompa','v3zS','gSkRkq','b8oWbG','AmoebW','W6tdQXy','nSopWPW','WPG0Dq','W6FcVWO','W53cTHm','tmkOW40','dNVdOW','bSkkEW','q8o4W4O','bmo2pW','W5ZdMbm','W6xcPwu','FXJdNa','vColWRG','aSoXWOW','WQLogG','qLvP','y8ovWPa','kCoOWQO','WPXCWRC','gmkLfG','rJhdHW','i8kFcq','WOa7W74','qgD2','b0RcVq','WP3cOmkR','W5ZcKw4','n3mi','WO3cT8o0','BCoCWPW','WRxcNSkY','WQ5wWRG','nmoCWR0','a8oVdq','sSoLaW','fSoZWOu','WRdcSYi','cCoFWOa','WQFcJa4','WQCeW7i','CmobWQS','sZOp','umkKW6G','dXNcVa','Bmolmq','fSo3WPS','WQGhW58','W6hcMbG','nCk7wG','WOPsdh3dJSkTsCkBW7PQWQlcIxe','mWFdRq','WPxcV8kY','BmkMW7y','tSoIWPS','WQ3cJXG','W6SYW60','emomEW','pmkVWQC','amkUcq'];_0x3ee4=function(){return _0x55659f;};return _0x3ee4();}")];
+ case 1:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+}
+exports.injectProgObserver = injectProgObserver;
+/**
+ * @private
+ */
+function injectInternalEventHandler(page) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, page.evaluate("console.log(\"internal_event_handler_injected\")")];
+ case 1:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ });
+}
+exports.injectInternalEventHandler = injectInternalEventHandler;
diff --git a/src/controllers/initializer.js b/src/controllers/initializer.js
new file mode 100644
index 0000000000..ea3615a023
--- /dev/null
+++ b/src/controllers/initializer.js
@@ -0,0 +1,687 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.create = exports.screenshot = exports.timeout = exports.configWithCases = exports.pkg = void 0;
+var fs = require("fs");
+var boxen_1 = require("boxen");
+var os_name_1 = require("os-name");
+var update_notifier_1 = require("update-notifier");
+var Client_1 = require("../api/Client");
+var index_1 = require("../api/model/index");
+var path = require("path");
+var os = require("os");
+var auth_1 = require("./auth");
+var browser_1 = require("./browser");
+var events_1 = require("./events");
+var launch_checks_1 = require("./launch_checks");
+var cfonts_1 = require("cfonts");
+var tools_1 = require("../utils/tools");
+var crypto_1 = require("crypto");
+var fs_extra_1 = require("fs-extra");
+var pico_s3_1 = require("pico-s3");
+var init_patch_1 = require("./init_patch");
+var patch_manager_1 = require("./patch_manager");
+var logging_1 = require("../logging/logging");
+var timeout = function (ms) {
+ return new Promise(function (resolve) { return setTimeout(resolve, ms, 'timeout'); });
+};
+exports.pkg = (0, fs_extra_1.readJsonSync)(path.join(__dirname, '../../package.json')), exports.configWithCases = (0, fs_extra_1.readJsonSync)(path.join(__dirname, '../../bin/config-schema.json')), exports.timeout = timeout;
+/**
+ * Used to initialize the client session.
+ *
+ * *Note* It is required to set all config variables as [ConfigObject](https://open-wa.github.io/wa-automate-nodejs/interfaces/configobject.html) that includes both [sessionId](https://open-wa.github.io/wa-automate-nodejs/interfaces/configobject.html#sessionId). Setting the session id as the first variable is no longer valid
+ *
+ * e.g
+ *
+ * ```javascript
+ * create({
+ * sessionId: 'main',
+ * customUserAgent: ' 'WhatsApp/2.16.352 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15',
+ * blockCrashLogs true,
+ * ...
+ * })....
+ * ```
+ * @param config AdvancedConfig The extended custom configuration
+ */
+//@ts-ignore
+function create(config) {
+ var _a, _b, _c, _d;
+ if (config === void 0) { config = {}; }
+ return __awaiter(this, void 0, void 0, function () {
+ var START_TIME, waPage, notifier, sessionId, customUserAgent, crossSpawn_1, result, prettyFont, popup, popupaddr, spinner, qrManager, RAM_INFO, PPTR_VERSION, mdDir, browserLaunchedTs, throwOnError, PAGE_UA, BROWSER_VERSION, OS, START_TS, screenshotPath_1, WA_AUTOMATE_VERSION, WA_VERSION, canInjectEarly, attemptingReauth, debugInfo, invariantAviodanceTs, authRace, authenticated, earlyWid, licensePromise, oorProms, outOfReach, race, to, result, tI, pre, VALID_SESSION, patchPromise, localStorage_1, _e, _f, stdSessionJsonPath, altMainModulePath, altSessionJsonPath, sessionjsonpath, sessionData, sdB64, error_1, pureWAPI, _g, _h, _j, NUM, LAUNCH_TIME_MS, metrics, purgedMessage, client, me, licIndex, _k, _l, _m, issueLink, _o, _p, _q, storeKeys, error_2;
+ var _this = this;
+ return __generator(this, function (_r) {
+ switch (_r.label) {
+ case 0:
+ START_TIME = Date.now();
+ if (config.logging) {
+ if (Array.isArray(config === null || config === void 0 ? void 0 : config.logging))
+ config.logging = (0, logging_1.setupLogging)(config === null || config === void 0 ? void 0 : config.logging, "owa-".concat((config === null || config === void 0 ? void 0 : config.sessionId) || 'session'));
+ }
+ waPage = undefined;
+ sessionId = '';
+ if (!config || (config === null || config === void 0 ? void 0 : config.eventMode) !== false) {
+ config.eventMode = true;
+ }
+ if ((config === null || config === void 0 ? void 0 : config.waitForRipeSession) !== false)
+ config.waitForRipeSession = true;
+ if ((config === null || config === void 0 ? void 0 : config.multiDevice) !== false)
+ config.multiDevice = true;
+ if ((config === null || config === void 0 ? void 0 : config.deleteSessionDataOnLogout) !== false)
+ config.deleteSessionDataOnLogout = true;
+ if (!(!(config === null || config === void 0 ? void 0 : config.skipUpdateCheck) || (config === null || config === void 0 ? void 0 : config.keepUpdated))) return [3 /*break*/, 3];
+ return [4 /*yield*/, (0, update_notifier_1["default"])({
+ pkg: exports.pkg,
+ updateCheckInterval: 0
+ })];
+ case 1:
+ notifier = _r.sent();
+ notifier.notify();
+ if (!((notifier === null || notifier === void 0 ? void 0 : notifier.update) && (config === null || config === void 0 ? void 0 : config.keepUpdated) && (notifier === null || notifier === void 0 ? void 0 : notifier.update.latest) !== exports.pkg.version)) return [3 /*break*/, 3];
+ console.log('UPDATING @OPEN-WA');
+ logging_1.log.info('UPDATING @OPEN-WA');
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('cross-spawn'); })];
+ case 2:
+ crossSpawn_1 = _r.sent();
+ result = crossSpawn_1.sync('npm', ['i', '@open-wa/wa-automate'], { stdio: 'inherit' });
+ if (!result.stderr) {
+ console.log('UPDATED SUCCESSFULLY');
+ logging_1.log.info('UPDATED SUCCESSFULLY');
+ }
+ console.log('RESTARTING PROCESS');
+ logging_1.log.info('RESTARTING PROCESS');
+ process.on("exit", function () {
+ crossSpawn_1.spawn(process.argv.shift(), process.argv, {
+ cwd: process.cwd(),
+ detached: true,
+ stdio: "inherit"
+ });
+ });
+ process.exit();
+ _r.label = 3;
+ case 3:
+ if (config === null || config === void 0 ? void 0 : config.inDocker) {
+ //try to infer config variables from process.env
+ config = __assign(__assign({}, config), (0, tools_1.getConfigFromProcessEnv)(exports.configWithCases));
+ config.chromiumArgs = (config === null || config === void 0 ? void 0 : config.chromiumArgs) || [];
+ customUserAgent = config.customUserAgent;
+ }
+ if (sessionId === '' || (config === null || config === void 0 ? void 0 : config.sessionId))
+ sessionId = (config === null || config === void 0 ? void 0 : config.sessionId) || 'session';
+ prettyFont = cfonts_1["default"].render(('@OPEN-WA|WHATSAPP|AUTOMATOR'), {
+ font: '3d',
+ color: 'candy',
+ align: 'center',
+ gradient: ["red", "#f80"],
+ lineHeight: 3
+ });
+ console.log((config === null || config === void 0 ? void 0 : config.disableSpins) ? (0, boxen_1["default"])([
+ "@open-wa/wa-automate ",
+ "".concat(exports.pkg.description),
+ "Version: ".concat(exports.pkg.version, " "),
+ "Check out the latest changes: https://github.com/open-wa/wa-automate-nodejs#latest-changes ",
+ ].join('\n'), { padding: 1, borderColor: 'yellow', borderStyle: 'bold' }) : prettyFont.string);
+ if (!(config === null || config === void 0 ? void 0 : config.popup)) return [3 /*break*/, 6];
+ return [4 /*yield*/, Promise.resolve().then(function () { return require('./popup'); })];
+ case 4:
+ popup = (_r.sent()).popup;
+ return [4 /*yield*/, popup(config)];
+ case 5:
+ popupaddr = _r.sent();
+ console.log("You can also authenticate the session at: ".concat(popupaddr));
+ logging_1.log.info("You can also authenticate the session at: ".concat(popupaddr));
+ _r.label = 6;
+ case 6:
+ if (!sessionId)
+ sessionId = 'session';
+ spinner = new events_1.Spin(sessionId, 'STARTUP', config === null || config === void 0 ? void 0 : config.disableSpins);
+ qrManager = new auth_1.QRManager(__assign(__assign({}, config), { qrLogSkip: true }));
+ RAM_INFO = "Total: ".concat(parseFloat("".concat(os.totalmem() / 1000000000)).toFixed(2), " GB | Free: ").concat(parseFloat("".concat(os.freemem() / 1000000000)).toFixed(2), " GB");
+ logging_1.log.info("RAM INFO", RAM_INFO);
+ PPTR_VERSION = ((_a = (0, fs_extra_1.readJsonSync)(require.resolve("puppeteer/package.json"), { throws: false })) === null || _a === void 0 ? void 0 : _a.version) || "UNKNOWN";
+ logging_1.log.info("PPTR VERSION INFO", PPTR_VERSION);
+ _r.label = 7;
+ case 7:
+ _r.trys.push([7, 83, , 85]);
+ if (typeof config === 'string')
+ console.error("AS OF VERSION 3+ YOU CAN NO LONGER SET THE SESSION ID AS THE FIRST PARAMETER OF CREATE. CREATE CAN ONLY TAKE A CONFIG OBJECT. IF YOU STILL HAVE CONFIGS AS A SECOND PARAMETER, THEY WILL HAVE NO EFFECT! PLEASE SEE DOCS.");
+ spinner.start('Starting');
+ spinner.succeed("Version: ".concat(exports.pkg.version));
+ spinner.info("Initializing WA");
+ mdDir = config["userDataDir"] || "".concat((config === null || config === void 0 ? void 0 : config.sessionDataPath) || ((config === null || config === void 0 ? void 0 : config.inDocker) ? '/sessions' : (config === null || config === void 0 ? void 0 : config.sessionDataPath) || '.'), "/_IGNORE_").concat((config === null || config === void 0 ? void 0 : config.sessionId) || 'session');
+ if (process.env.AUTO_MD && fs.existsSync(mdDir) && !(config === null || config === void 0 ? void 0 : config.multiDevice)) {
+ spinner.info("Multi-Device directory detected. multiDevice set to true.");
+ config.multiDevice = true;
+ }
+ if ((config === null || config === void 0 ? void 0 : config.multiDevice) && (config === null || config === void 0 ? void 0 : config.chromiumArgs))
+ spinner.info("Using custom chromium args with multi device will cause issues! Please remove them: ".concat(config === null || config === void 0 ? void 0 : config.chromiumArgs));
+ if ((config === null || config === void 0 ? void 0 : config.multiDevice) && !(config === null || config === void 0 ? void 0 : config.useChrome))
+ spinner.info("It is recommended to set useChrome: true or use the --use-chrome flag if you are experiencing issues with Multi device support");
+ return [4 /*yield*/, (0, browser_1.initPage)(sessionId, config, qrManager, customUserAgent, spinner)];
+ case 8:
+ waPage = _r.sent();
+ spinner.succeed('Page loaded');
+ browserLaunchedTs = (0, tools_1.now)();
+ throwOnError = config && config.throwErrorOnTosBlock == true;
+ return [4 /*yield*/, waPage.evaluate('navigator.userAgent')];
+ case 9:
+ PAGE_UA = _r.sent();
+ return [4 /*yield*/, waPage.browser().version()];
+ case 10:
+ BROWSER_VERSION = _r.sent();
+ OS = (0, os_name_1["default"])();
+ START_TS = Date.now();
+ screenshotPath_1 = "./logs/".concat(config.sessionId || 'session', "/").concat(START_TS);
+ exports.screenshot = function (page) { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, page.screenshot({
+ path: "".concat(screenshotPath_1, "/").concat(Date.now(), ".jpg")
+ })["catch"](function () {
+ fs.mkdirSync(screenshotPath_1, { recursive: true });
+ return (0, exports.screenshot)(page);
+ })];
+ case 1:
+ _a.sent();
+ console.log('Screenshot taken. path:', "".concat(screenshotPath_1));
+ return [2 /*return*/];
+ }
+ });
+ }); };
+ if (config === null || config === void 0 ? void 0 : config.screenshotOnInitializationBrowserError)
+ waPage.on('console', function (msg) { return __awaiter(_this, void 0, void 0, function () {
+ var i;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ for (i = 0; i < msg.args().length; ++i)
+ console.log("".concat(i, ": ").concat(msg.args()[i]));
+ if (!(msg.type() === 'error' && !msg.text().includes('apify') && !msg.text().includes('crashlogs'))) return [3 /*break*/, 2];
+ return [4 /*yield*/, (0, exports.screenshot)(waPage)];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2: return [2 /*return*/];
+ }
+ });
+ }); });
+ WA_AUTOMATE_VERSION = "".concat(exports.pkg.version).concat((notifier === null || notifier === void 0 ? void 0 : notifier.update) && ((notifier === null || notifier === void 0 ? void 0 : notifier.update.latest) !== exports.pkg.version) ? " UPDATE AVAILABLE: ".concat(notifier === null || notifier === void 0 ? void 0 : notifier.update.latest) : '');
+ return [4 /*yield*/, waPage.waitForFunction('window.Debug!=undefined && window.Debug.VERSION!=undefined && require')];
+ case 11:
+ _r.sent();
+ return [4 /*yield*/, waPage.evaluate(function () { return window.Debug ? window.Debug.VERSION : 'I think you have been TOS_BLOCKed'; })];
+ case 12:
+ WA_VERSION = _r.sent();
+ return [4 /*yield*/, (0, patch_manager_1.earlyInjectionCheck)(waPage)];
+ case 13:
+ canInjectEarly = _r.sent();
+ return [4 /*yield*/, waPage.evaluate("!!(localStorage['WAToken2'] || localStorage['last-wid-md'])")];
+ case 14:
+ attemptingReauth = _r.sent();
+ debugInfo = {
+ WA_VERSION: WA_VERSION,
+ PAGE_UA: PAGE_UA,
+ WA_AUTOMATE_VERSION: WA_AUTOMATE_VERSION,
+ BROWSER_VERSION: BROWSER_VERSION,
+ OS: OS,
+ START_TS: START_TS,
+ RAM_INFO: RAM_INFO,
+ PPTR_VERSION: PPTR_VERSION
+ };
+ if ((config === null || config === void 0 ? void 0 : config.logDebugInfoAsObject) || (config === null || config === void 0 ? void 0 : config.disableSpins))
+ spinner.succeed("Debug info: ".concat(JSON.stringify(debugInfo, null, 2)));
+ else {
+ console.table(debugInfo);
+ logging_1.log.info('Debug info:', debugInfo);
+ }
+ debugInfo.LATEST_VERSION = !((notifier === null || notifier === void 0 ? void 0 : notifier.update) && ((notifier === null || notifier === void 0 ? void 0 : notifier.update.latest) !== exports.pkg.version));
+ debugInfo.CLI = process.env.OWA_CLI && true || false;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ spinner.succeed('Use this easy pre-filled link to report an issue: ' + (0, tools_1.generateGHIssueLink)(config, debugInfo));
+ spinner.info("Time to injection: ".concat(((0, tools_1.now)() - browserLaunchedTs).toFixed(0), "ms"));
+ invariantAviodanceTs = (0, tools_1.now)();
+ return [4 /*yield*/, Promise.race([
+ waPage.waitForFunction("(()=>{return require && require(\"__debug\").modulesMap[\"WAWebCollections\"] ? true : false})()", { timeout: 10000 })["catch"](function () { }),
+ waPage.waitForFunction("[...document.getElementsByTagName('div')].filter(x=>x.dataset && x.dataset.testid)[0]?.dataset?.testid === 'qrcode'", { timeout: 10000 })["catch"](function () { }),
+ waPage.waitForFunction("document.getElementsByTagName('circle').length===1", { timeout: 10000 })["catch"](function () { }) //qr spinner is present
+ ])];
+ case 15:
+ _r.sent();
+ spinner.info("Invariant Violation Avoidance: ".concat(((0, tools_1.now)() - invariantAviodanceTs).toFixed(0), "ms"));
+ if (!canInjectEarly) return [3 /*break*/, 19];
+ if (!attemptingReauth) return [3 /*break*/, 17];
+ return [4 /*yield*/, waPage.evaluate("window.Store = {\"Msg\": true}")];
+ case 16:
+ _r.sent();
+ _r.label = 17;
+ case 17:
+ spinner.start('Injecting api');
+ return [4 /*yield*/, (0, browser_1.injectApi)(waPage, spinner)];
+ case 18:
+ waPage = _r.sent();
+ spinner.start('WAPI injected');
+ return [3 /*break*/, 20];
+ case 19:
+ spinner.remove();
+ if (throwOnError)
+ throw Error('TOSBLOCK');
+ _r.label = 20;
+ case 20:
+ spinner.start('Authenticating');
+ authRace = [];
+ authRace.push((0, auth_1.isAuthenticated)(waPage)["catch"](function () { }));
+ if ((config === null || config === void 0 ? void 0 : config.authTimeout) !== 0) {
+ authRace.push((0, exports.timeout)((config.authTimeout || config.multiDevice ? 120 : 60) * 1000));
+ }
+ return [4 /*yield*/, Promise.race(authRace)];
+ case 21:
+ authenticated = _r.sent();
+ if (!(authenticated === 'NUKE' && !(config === null || config === void 0 ? void 0 : config.ignoreNuke))) return [3 /*break*/, 25];
+ //kill the browser
+ spinner.fail("Session data most likely expired due to manual host account logout. Please re-authenticate this session.");
+ return [4 /*yield*/, (0, browser_1.kill)(waPage)];
+ case 22:
+ _r.sent();
+ if (!(config === null || config === void 0 ? void 0 : config.deleteSessionDataOnLogout)) return [3 /*break*/, 24];
+ return [4 /*yield*/, (0, browser_1.deleteSessionData)(config)];
+ case 23:
+ _r.sent();
+ _r.label = 24;
+ case 24:
+ if (config === null || config === void 0 ? void 0 : config.throwOnExpiredSessionData) {
+ throw new index_1.SessionExpiredError();
+ }
+ else
+ //restart the process with no session data
+ return [2 /*return*/, create(__assign(__assign({}, config), { sessionData: authenticated }))];
+ _r.label = 25;
+ case 25: return [4 /*yield*/, waPage.evaluate("(localStorage[\"last-wid\"] || '').replace(/\"/g,\"\")")];
+ case 26:
+ earlyWid = _r.sent();
+ licensePromise = (0, patch_manager_1.getLicense)(config, {
+ _serialized: earlyWid
+ }, debugInfo, spinner);
+ if (!(authenticated == 'timeout')) return [3 /*break*/, 29];
+ oorProms = [(0, auth_1.phoneIsOutOfReach)(waPage)];
+ if ((config === null || config === void 0 ? void 0 : config.oorTimeout) !== 0)
+ oorProms.push((0, exports.timeout)(((config === null || config === void 0 ? void 0 : config.oorTimeout) || 60) * 1000));
+ return [4 /*yield*/, Promise.race(oorProms)];
+ case 27:
+ outOfReach = _r.sent();
+ spinner.emit(outOfReach && outOfReach !== 'timeout' ? 'appOffline' : 'authTimeout');
+ spinner.fail(outOfReach && outOfReach !== 'timeout' ? 'Authentication timed out. Please open the app on the phone. Shutting down' : 'Authentication timed out. Shutting down. Consider increasing authTimeout config variable: https://open-wa.github.io/wa-automate-nodejs/interfaces/configobject.html#authtimeout');
+ return [4 /*yield*/, (0, browser_1.kill)(waPage)];
+ case 28:
+ _r.sent();
+ if (config === null || config === void 0 ? void 0 : config.killProcessOnTimeout)
+ process.exit();
+ throw new Error(outOfReach ? 'App Offline' : 'Auth Timeout. Consider increasing authTimeout config variable: https://open-wa.github.io/wa-automate-nodejs/interfaces/configobject.html#authtimeout');
+ case 29:
+ if (!authenticated) return [3 /*break*/, 30];
+ spinner.succeed('Authenticated');
+ return [3 /*break*/, 36];
+ case 30:
+ spinner.info('Authenticate to continue');
+ race = [];
+ if (config === null || config === void 0 ? void 0 : config.linkCode) {
+ race.push(qrManager.linkCode(waPage, config, spinner));
+ }
+ else
+ race.push(qrManager.smartQr(waPage, config, spinner));
+ if ((config === null || config === void 0 ? void 0 : config.qrTimeout) !== 0) {
+ to = ((config === null || config === void 0 ? void 0 : config.qrTimeout) || 60) * 1000;
+ if (config === null || config === void 0 ? void 0 : config.multiDevice)
+ to = to * 2;
+ race.push((0, exports.timeout)(to));
+ }
+ return [4 /*yield*/, Promise.race(race)];
+ case 31:
+ result = _r.sent();
+ if (!(result === "MULTI_DEVICE_DETECTED" && !(config === null || config === void 0 ? void 0 : config.multiDevice))) return [3 /*break*/, 33];
+ return [4 /*yield*/, (0, browser_1.kill)(waPage)];
+ case 32:
+ _r.sent();
+ return [2 /*return*/, create(__assign(__assign({}, config), { multiDevice: true }))];
+ case 33:
+ if (!(result == 'timeout')) return [3 /*break*/, 35];
+ spinner.emit('qrTimeout');
+ spinner.fail('QR scan took too long. Session Timed Out. Shutting down. Consider increasing qrTimeout config variable: https://open-wa.github.io/wa-automate-nodejs/interfaces/configobject.html#qrtimeout');
+ return [4 /*yield*/, (0, browser_1.kill)(waPage)];
+ case 34:
+ _r.sent();
+ if (config === null || config === void 0 ? void 0 : config.killProcessOnTimeout)
+ process.exit();
+ throw new Error('QR Timeout');
+ case 35:
+ spinner.emit('successfulScan');
+ spinner.succeed();
+ _r.label = 36;
+ case 36:
+ if (!config.logInternalEvents) return [3 /*break*/, 38];
+ return [4 /*yield*/, waPage.evaluate("debugEvents=true")];
+ case 37:
+ _r.sent();
+ _r.label = 38;
+ case 38: return [4 /*yield*/, waPage.evaluate("window.critlis=true")];
+ case 39:
+ _r.sent();
+ return [4 /*yield*/, (0, tools_1.timePromise)(function () { return (0, init_patch_1.injectInternalEventHandler)(waPage); })];
+ case 40:
+ tI = _r.sent();
+ logging_1.log.info("Injected internal event handler: ".concat(tI, " ms"));
+ if (!attemptingReauth) return [3 /*break*/, 43];
+ return [4 /*yield*/, waPage.evaluate("window.Store = undefined")];
+ case 41:
+ _r.sent();
+ if (!(config === null || config === void 0 ? void 0 : config.waitForRipeSession)) return [3 /*break*/, 43];
+ spinner.start("Waiting for ripe session....");
+ return [4 /*yield*/, (0, auth_1.waitForRipeSession)(waPage, config === null || config === void 0 ? void 0 : config.waitForRipeSessionTimeout)];
+ case 42:
+ if (_r.sent())
+ spinner.succeed("Session ready for injection");
+ else
+ spinner.fail("You may experience issues in headless mode. Continuing...");
+ _r.label = 43;
+ case 43:
+ pre = canInjectEarly ? 'Rei' : 'I';
+ spinner.start("".concat(pre, "njecting api"));
+ return [4 /*yield*/, (0, browser_1.injectApi)(waPage, spinner, true)];
+ case 44:
+ waPage = _r.sent();
+ spinner.succeed("WAPI ".concat(pre, "njected"));
+ if (!canInjectEarly) return [3 /*break*/, 47];
+ //check if page is valid after 5 seconds
+ spinner.start('Checking if session is valid');
+ if (!(config === null || config === void 0 ? void 0 : config.safeMode)) return [3 /*break*/, 47];
+ return [4 /*yield*/, (0, exports.timeout)(5000)];
+ case 45:
+ _r.sent();
+ return [4 /*yield*/, (0, browser_1.injectApi)(waPage, spinner, true)];
+ case 46:
+ _r.sent();
+ _r.label = 47;
+ case 47: return [4 /*yield*/, waPage.waitForFunction("window.Store && window.Store.Msg ? true : false", { timeout: 9000, polling: 200 })["catch"](function (e) { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ logging_1.log.error("Valid session check failed", e);
+ return [2 /*return*/, false];
+ });
+ }); })];
+ case 48:
+ VALID_SESSION = _r.sent();
+ if (!VALID_SESSION) return [3 /*break*/, 78];
+ patchPromise = (0, patch_manager_1.getPatch)(config, spinner, debugInfo);
+ spinner.succeed('Client is ready');
+ _f = (_e = JSON).parse;
+ return [4 /*yield*/, waPage.evaluate(function () {
+ return JSON.stringify(window.localStorage);
+ })];
+ case 49:
+ localStorage_1 = _f.apply(_e, [_r.sent()]);
+ stdSessionJsonPath = ((config === null || config === void 0 ? void 0 : config.sessionDataPath) && (config === null || config === void 0 ? void 0 : config.sessionDataPath.includes('.data.json'))) ? path.join(path.resolve(process.cwd(), (config === null || config === void 0 ? void 0 : config.sessionDataPath) || '')) : path.join(path.resolve(process.cwd(), (config === null || config === void 0 ? void 0 : config.sessionDataPath) || ''), "".concat(sessionId || 'session', ".data.json"));
+ altMainModulePath = ((_b = require === null || require === void 0 ? void 0 : require.main) === null || _b === void 0 ? void 0 : _b.path) || ((_c = process === null || process === void 0 ? void 0 : process.mainModule) === null || _c === void 0 ? void 0 : _c.path);
+ altSessionJsonPath = !altMainModulePath ? null : ((config === null || config === void 0 ? void 0 : config.sessionDataPath) && (config === null || config === void 0 ? void 0 : config.sessionDataPath.includes('.data.json'))) ? path.join(path.resolve(altMainModulePath, (config === null || config === void 0 ? void 0 : config.sessionDataPath) || '')) : path.join(path.resolve(altMainModulePath, (config === null || config === void 0 ? void 0 : config.sessionDataPath) || ''), "".concat(sessionId || 'session', ".data.json"));
+ sessionjsonpath = altSessionJsonPath && fs.existsSync(altSessionJsonPath) ? altSessionJsonPath : stdSessionJsonPath;
+ sessionData = {
+ WABrowserId: localStorage_1.WABrowserId,
+ WASecretBundle: localStorage_1.WASecretBundle,
+ WAToken1: localStorage_1.WAToken1,
+ WAToken2: localStorage_1.WAToken2
+ };
+ if (config.multiDevice) {
+ delete sessionData.WABrowserId;
+ logging_1.log.info("Multi-device detected. Removing Browser ID from session data to prevent session reauth corruption");
+ }
+ sdB64 = Buffer.from(JSON.stringify(sessionData)).toString('base64');
+ spinner.emit(sessionData, "sessionData");
+ spinner.emit(sdB64, "sessionDataBase64");
+ if (!(config === null || config === void 0 ? void 0 : config.skipSessionSave))
+ fs.writeFile(sessionjsonpath, sdB64, function (err) {
+ if (err) {
+ console.error(err);
+ return;
+ }
+ });
+ if (!(config === null || config === void 0 ? void 0 : config.sessionDataBucketAuth)) return [3 /*break*/, 53];
+ _r.label = 50;
+ case 50:
+ _r.trys.push([50, 52, , 53]);
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Uploading new session data to cloud storage..');
+ return [4 /*yield*/, (0, pico_s3_1.upload)(__assign(__assign({ directory: '_sessionData' }, JSON.parse(Buffer.from(config.sessionDataBucketAuth, 'base64').toString('ascii'))), { filename: "".concat(config.sessionId || 'session', ".data.json"), file: "data:text/plain;base64,".concat(Buffer.from(sdB64).toString('base64')) }))];
+ case 51:
+ _r.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed('Successfully uploaded session data file to cloud storage!');
+ return [3 /*break*/, 53];
+ case 52:
+ error_1 = _r.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail("Something went wrong while uploading new session data to cloud storage bucket. Continuing...");
+ return [3 /*break*/, 53];
+ case 53:
+ /**
+ * Set page-level logging
+ */
+ waPage.on('console', function (msg) {
+ if (config === null || config === void 0 ? void 0 : config.logConsole)
+ console.log(msg);
+ logging_1.log.info('Page Console:', msg.text());
+ });
+ waPage.on('error', function (error) {
+ if (config === null || config === void 0 ? void 0 : config.logConsoleErrors)
+ console.error(error);
+ logging_1.log.error('Page Console Error:', error.message || (error === null || error === void 0 ? void 0 : error.text()));
+ });
+ if (config === null || config === void 0 ? void 0 : config.restartOnCrash)
+ waPage.on('error', function (error) { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ console.error('Page Crashed! Restarting...', error);
+ return [4 /*yield*/, (0, browser_1.kill)(waPage)];
+ case 1:
+ _a.sent();
+ return [4 /*yield*/, create(config).then(config.restartOnCrash)];
+ case 2:
+ _a.sent();
+ return [2 /*return*/];
+ }
+ });
+ }); });
+ return [4 /*yield*/, (0, launch_checks_1.checkWAPIHash)()];
+ case 54:
+ pureWAPI = _r.sent();
+ if (!pureWAPI) {
+ config.skipBrokenMethodsCheck = true;
+ // config.skipPatches = true;
+ }
+ if (!(config === null || config === void 0 ? void 0 : config.hostNotificationLang)) return [3 /*break*/, 56];
+ return [4 /*yield*/, waPage.evaluate("window.hostlang=\"".concat(config.hostNotificationLang, "\""))];
+ case 55:
+ _r.sent();
+ _r.label = 56;
+ case 56:
+ if (!!(config === null || config === void 0 ? void 0 : config.skipPatches)) return [3 /*break*/, 60];
+ _g = patch_manager_1.getAndInjectLivePatch;
+ _h = [waPage, spinner];
+ return [4 /*yield*/, patchPromise];
+ case 57: return [4 /*yield*/, _g.apply(void 0, _h.concat([_r.sent(), config, debugInfo]))];
+ case 58:
+ _r.sent();
+ _j = debugInfo;
+ return [4 /*yield*/, waPage.evaluate("window.o()")];
+ case 59:
+ _j.OW_KEY = _r.sent();
+ _r.label = 60;
+ case 60: return [4 /*yield*/, waPage.evaluate("(window.moi() || \"\").replace('@c.us','').replace(/\"/g,\"\")")];
+ case 61:
+ NUM = ((_r.sent()) || "");
+ debugInfo.NUM = NUM.slice(-4);
+ debugInfo.NUM_HASH = (0, crypto_1.createHash)('md5').update(NUM, 'utf8').digest('hex');
+ if (!((config === null || config === void 0 ? void 0 : config.skipBrokenMethodsCheck) !== true)) return [3 /*break*/, 63];
+ return [4 /*yield*/, (0, launch_checks_1.integrityCheck)(waPage, notifier, spinner, debugInfo)];
+ case 62:
+ _r.sent();
+ _r.label = 63;
+ case 63:
+ LAUNCH_TIME_MS = Date.now() - START_TIME;
+ debugInfo = __assign(__assign({}, debugInfo), { LAUNCH_TIME_MS: LAUNCH_TIME_MS });
+ spinner.emit(debugInfo, "DebugInfo");
+ return [4 /*yield*/, waPage.evaluate(function (_a) {
+ var config = _a.config;
+ return WAPI.launchMetrics(config);
+ }, { config: config })];
+ case 64:
+ metrics = _r.sent();
+ purgedMessage = (metrics === null || metrics === void 0 ? void 0 : metrics.purged) ? Object.entries(metrics.purged).filter(function (_a) {
+ var e = _a[1];
+ return Number(e) > 0;
+ }).map(function (_a) {
+ var k = _a[0], e = _a[1];
+ return "".concat(e, " ").concat(k);
+ }).join(" and ") : "";
+ if (metrics.isMd && !(config === null || config === void 0 ? void 0 : config.multiDevice))
+ spinner.info("!!!Please set multiDevice: true in the config or use the --mutli-Device flag!!!");
+ spinner.succeed("Client loaded for ".concat(metrics.isBiz ? "business" : "normal", " account ").concat(metrics.isMd && "[MD] " || '', "with ").concat(metrics.contacts, " contacts, ").concat(metrics.chats, " chats & ").concat(metrics.messages, " messages ").concat(purgedMessage ? "+ purged ".concat(purgedMessage, " ") : "", "in ").concat(LAUNCH_TIME_MS / 1000, "s"));
+ debugInfo.ACC_TYPE = metrics.isBiz ? "BUSINESS" : "PERSONAL";
+ if ((config === null || config === void 0 ? void 0 : config.deleteSessionDataOnLogout) || (config === null || config === void 0 ? void 0 : config.killClientOnLogout))
+ config.eventMode = true;
+ client = new Client_1.Client(waPage, config, __assign(__assign({}, debugInfo), metrics));
+ return [4 /*yield*/, client.getMe()];
+ case 65:
+ me = (_r.sent()).me;
+ licIndex = process.argv.findIndex(function (arg) { return arg === "--license-key" || arg === "-l"; });
+ config.licenseKey = config.licenseKey || licIndex !== -1 && process.argv[licIndex + 1];
+ if (!((config === null || config === void 0 ? void 0 : config.licenseKey) || me._serialized !== earlyWid)) return [3 /*break*/, 70];
+ _k = patch_manager_1.getAndInjectLicense;
+ _l = [waPage, config, me, debugInfo, spinner];
+ if (!(me._serialized !== earlyWid)) return [3 /*break*/, 66];
+ _m = false;
+ return [3 /*break*/, 68];
+ case 66: return [4 /*yield*/, licensePromise];
+ case 67:
+ _m = _r.sent();
+ _r.label = 68;
+ case 68: return [4 /*yield*/, _k.apply(void 0, _l.concat([_m]))];
+ case 69:
+ _r.sent();
+ _r.label = 70;
+ case 70:
+ spinner.info("Finalizing web session...");
+ return [4 /*yield*/, (0, init_patch_1.injectInitPatch)(waPage)];
+ case 71:
+ _r.sent();
+ spinner.info("Finalizing client...");
+ return [4 /*yield*/, client.loaded()];
+ case 72:
+ _r.sent();
+ if (!(config.ensureHeadfulIntegrity && !attemptingReauth)) return [3 /*break*/, 74];
+ spinner.info("QR scanned for the first time. Refreshing...");
+ return [4 /*yield*/, client.refresh()];
+ case 73:
+ _r.sent();
+ spinner.info("Session refreshed.");
+ _r.label = 74;
+ case 74: return [4 /*yield*/, client.getIssueLink()];
+ case 75:
+ issueLink = _r.sent();
+ console.log((0, boxen_1["default"])("Use the link below to easily report issues:👇👇👇", { padding: 1, borderColor: 'red' }));
+ spinner.succeed(issueLink);
+ spinner.succeed("\uD83D\uDE80 @OPEN-WA ready for account: ".concat(me.user.slice(-4)));
+ if (!(!debugInfo.CLI && !config.licenseKey)) return [3 /*break*/, 77];
+ _p = (_o = spinner).succeed;
+ _q = "Use this link to get a license: ".concat;
+ return [4 /*yield*/, client.getLicenseLink()];
+ case 76:
+ _p.apply(_o, [_q.apply("Use this link to get a license: ", [_r.sent()])]);
+ _r.label = 77;
+ case 77:
+ spinner.emit('SUCCESS');
+ spinner.remove();
+ return [2 /*return*/, client];
+ case 78: return [4 /*yield*/, waPage.evaluate("Object.keys(window.Store || {})")];
+ case 79:
+ storeKeys = _r.sent();
+ logging_1.log.info("Store keys", storeKeys);
+ spinner.fail('The session is invalid. Retrying');
+ return [4 /*yield*/, (0, browser_1.kill)(waPage)];
+ case 80:
+ _r.sent();
+ return [4 /*yield*/, create(config)];
+ case 81: return [2 /*return*/, _r.sent()];
+ case 82: return [3 /*break*/, 85];
+ case 83:
+ error_2 = _r.sent();
+ spinner.emit(error_2.message);
+ logging_1.log.error(error_2.message);
+ if (error_2.stack) {
+ logging_1.log.error(error_2.stack);
+ console.error(error_2.stack);
+ }
+ return [4 /*yield*/, (0, browser_1.kill)(waPage)];
+ case 84:
+ _r.sent();
+ if (error_2.name === "ProtocolError" && ((_d = error_2.message) === null || _d === void 0 ? void 0 : _d.includes("Target closed"))) {
+ spinner.fail(error_2.message);
+ process.exit();
+ }
+ if (error_2.name === "TimeoutError" && (config === null || config === void 0 ? void 0 : config.multiDevice)) {
+ spinner.fail("Please delete the ".concat(config === null || config === void 0 ? void 0 : config.userDataDir, " folder and any related data.json files and try again. It is highly suggested to set useChrome: true also."));
+ }
+ if (error_2.name === "TimeoutError" && (config === null || config === void 0 ? void 0 : config.killProcessOnTimeout)) {
+ process.exit();
+ }
+ else {
+ spinner.remove();
+ throw error_2;
+ }
+ return [3 /*break*/, 85];
+ case 85: return [2 /*return*/];
+ }
+ });
+ });
+}
+exports.create = create;
diff --git a/src/controllers/initializer.ts b/src/controllers/initializer.ts
index a8e9bf76de..1cbb57499a 100755
--- a/src/controllers/initializer.ts
+++ b/src/controllers/initializer.ts
@@ -130,7 +130,8 @@ export async function create(config: AdvancedConfig | ConfigObject = {}): Promis
}
if (!sessionId) sessionId = 'session';
const spinner = new Spin(sessionId, 'STARTUP', config?.disableSpins);
- const qrManager = new QRManager(config);
+ // 强制让 QRManager 不在控制台打印二维码,以确保服务器以静默模式启动
+ const qrManager = new QRManager({ ...config, qrLogSkip: true });
const RAM_INFO = `Total: ${parseFloat(`${os.totalmem() / 1000000000}`).toFixed(2)} GB | Free: ${parseFloat(`${os.freemem() / 1000000000}`).toFixed(2)} GB`
log.info("RAM INFO", RAM_INFO)
const PPTR_VERSION = readJsonSync(require.resolve("puppeteer/package.json"), {throws:false})?.version || "UNKNOWN";
diff --git a/src/controllers/launch_checks.js b/src/controllers/launch_checks.js
new file mode 100644
index 0000000000..531e6f7fd0
--- /dev/null
+++ b/src/controllers/launch_checks.js
@@ -0,0 +1,189 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.integrityCheck = exports.checkWAPIHash = void 0;
+var path = require("path");
+var hasha_1 = require("hasha");
+var lodash_uniq_1 = require("lodash.uniq");
+var fs_extra_1 = require("fs-extra");
+var fs = require("fs");
+var pkg = (0, fs_extra_1.readJsonSync)(path.join(__dirname, '../../package.json'));
+var currentHash = '8d3a09fe3156605ac2cf55ce920bbbab';
+function checkWAPIHash() {
+ return __awaiter(this, void 0, void 0, function () {
+ var h;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, hasha_1["default"].fromFile(path.join(__dirname, '../lib', 'wapi.js'), { algorithm: 'md5' })];
+ case 1:
+ h = _a.sent();
+ return [2 /*return*/, h == currentHash];
+ }
+ });
+ });
+}
+exports.checkWAPIHash = checkWAPIHash;
+function integrityCheck(waPage, notifier, spinner, debugInfo) {
+ return __awaiter(this, void 0, void 0, function () {
+ var waitForIdle, wapi, methods, check, BROKEN_METHODS, unconditionalInject, axios, report;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ waitForIdle = catchRequests(waPage);
+ spinner.start('Checking client integrity');
+ return [4 /*yield*/, waitForIdle()];
+ case 1:
+ _a.sent();
+ wapi = fs.readFileSync(path.join(__dirname, '../lib', 'wapi.js'), 'utf8');
+ methods = (0, lodash_uniq_1["default"])(wapi.match(/(Store[.\w]*)\(/g).map(function (x) { return x.replace("(", ""); }));
+ check = function () { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, waPage.evaluate(function (checkList) {
+ return checkList.filter(function (check) {
+ try {
+ return eval(check) ? false : true;
+ }
+ catch (error) {
+ return true;
+ }
+ });
+ }, methods)];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+ }); };
+ return [4 /*yield*/, check()];
+ case 2:
+ BROKEN_METHODS = _a.sent();
+ if (!(BROKEN_METHODS.length > 0)) return [3 /*break*/, 13];
+ spinner.info('Broken methods detected. Attempting repair.');
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2500); })];
+ case 3:
+ _a.sent();
+ unconditionalInject = wapi.replace('!window.Store||!window.Store.Msg', 'true');
+ return [4 /*yield*/, waPage.evaluate(function (s) { return eval(s); }, unconditionalInject)];
+ case 4:
+ _a.sent();
+ return [4 /*yield*/, waitForIdle()];
+ case 5:
+ _a.sent();
+ return [4 /*yield*/, check()];
+ case 6:
+ //check again
+ BROKEN_METHODS = _a.sent();
+ if (!(BROKEN_METHODS.length > 0)) return [3 /*break*/, 11];
+ spinner.info('Unable to repair. Reporting broken methods.');
+ if (!(notifier === null || notifier === void 0 ? void 0 : notifier.update)) return [3 /*break*/, 7];
+ //needs an updated
+ spinner.fail("!!!BROKEN METHODS DETECTED!!!\n\n Please update to the latest version: " + notifier.update.latest);
+ return [3 /*break*/, 10];
+ case 7: return [4 /*yield*/, Promise.resolve().then(function () { return require('axios'); })];
+ case 8:
+ axios = (_a.sent())["default"];
+ return [4 /*yield*/, axios.post(pkg.brokenMethodReportUrl, __assign(__assign({}, debugInfo), { BROKEN_METHODS: BROKEN_METHODS }))["catch"](function () { return false; })];
+ case 9:
+ report = _a.sent();
+ if (report === null || report === void 0 ? void 0 : report.data) {
+ spinner.fail("Unable to repair broken methods. Sometimes this happens the first time after a new WA version, please try again. An issue has been created, add more detail if required: ".concat(report === null || report === void 0 ? void 0 : report.data));
+ }
+ else
+ spinner.fail("Unable to repair broken methods. Sometimes this happens the first time after a new WA version, please try again. Please check the issues in the repo for updates: https://github.com/open-wa/wa-automate-nodejs/issues");
+ _a.label = 10;
+ case 10: return [3 /*break*/, 12];
+ case 11:
+ spinner.info('Session repaired.');
+ _a.label = 12;
+ case 12: return [3 /*break*/, 14];
+ case 13:
+ spinner.info('Passed Integrity Test');
+ _a.label = 14;
+ case 14: return [2 /*return*/, true];
+ }
+ });
+ });
+}
+exports.integrityCheck = integrityCheck;
+function catchRequests(page, reqs) {
+ var _this = this;
+ if (reqs === void 0) { reqs = 0; }
+ var started = function () { return (reqs = reqs + 1); };
+ var ended = function () { return (reqs = reqs - 1); };
+ page.on('request', started);
+ page.on('requestfailed', ended);
+ page.on('requestfinished', ended);
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ return function (timeout, success) {
+ if (timeout === void 0) { timeout = 5000; }
+ if (success === void 0) { success = false; }
+ return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!true) return [3 /*break*/, 2];
+ if (reqs < 1)
+ return [3 /*break*/, 2];
+ return [4 /*yield*/, new Promise(function (yay) { return setTimeout(yay, 100); })];
+ case 1:
+ _a.sent();
+ if ((timeout = timeout - 100) < 0) {
+ throw new Error('Timeout');
+ }
+ return [3 /*break*/, 0];
+ case 2:
+ page.off('request', started);
+ page.off('requestfailed', ended);
+ page.off('requestfinished', ended);
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+}
diff --git a/src/controllers/patch_manager.js b/src/controllers/patch_manager.js
new file mode 100644
index 0000000000..c836e64e3a
--- /dev/null
+++ b/src/controllers/patch_manager.js
@@ -0,0 +1,320 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.getAndInjectLicense = exports.earlyInjectionCheck = exports.getLicense = exports.getAndInjectLivePatch = exports.injectLivePatch = exports.getPatch = void 0;
+var crypto = require("crypto");
+var events_1 = require("./events");
+var initializer_1 = require("./initializer");
+var axios_1 = require("axios");
+var fs_1 = require("fs");
+var PQueue = require("p-queue")["default"];
+var queue = new PQueue();
+/**
+ * @private
+ */
+function getPatch(config, spinner, sessionInfo) {
+ return __awaiter(this, void 0, void 0, function () {
+ var data, headers, ghUrl, hasSpin, patchFilePath, lastModifiedDate, patch, freshPatchFetchPromise;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ data = null;
+ headers = {};
+ ghUrl = "https://raw.githubusercontent.com/open-wa/wa-automate-nodejs/master/patches.json";
+ hasSpin = !!spinner;
+ patchFilePath = "".concat(process.cwd(), "/patches.ignore.data.json");
+ /**
+ * If cachedPatch is true then search for patch in current working directory.
+ */
+ if (config === null || config === void 0 ? void 0 : config.cachedPatch) {
+ spinner.info('Searching for cached patch');
+ // open file called patches.json and read as string
+ if ((0, fs_1.existsSync)(patchFilePath)) {
+ spinner.info('Found cached patch');
+ lastModifiedDate = (0, fs_1.statSync)(patchFilePath).mtimeMs;
+ /**
+ * Check if patchFilePath file is more than 1 day old
+ */
+ if ((lastModifiedDate + 86400000) < Date.now()) {
+ //this patch is stale.
+ spinner.fail('Cached patch is stale.');
+ }
+ else {
+ patch = (0, fs_1.readFileSync)(patchFilePath, 'utf8');
+ data = JSON.parse(patch);
+ spinner.info('Cached patch loaded');
+ }
+ }
+ else
+ spinner.fail('Cached patch not found');
+ }
+ freshPatchFetchPromise = function () { return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
+ var patchesBaseUrl, patchesUrl, START, _a, data, headers, END;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ patchesBaseUrl = (config === null || config === void 0 ? void 0 : config.ghPatch) ? ghUrl : initializer_1.pkg.patches;
+ patchesUrl = patchesBaseUrl + "?wv=".concat(sessionInfo.WA_VERSION, "&wav=").concat(sessionInfo.WA_AUTOMATE_VERSION);
+ if (!spinner)
+ spinner = new events_1.Spin(config.sessionId, "FETCH_PATCH", config.disableSpins, true);
+ spinner === null || spinner === void 0 ? void 0 : spinner.start("Downloading ".concat((config === null || config === void 0 ? void 0 : config.cachedPatch) ? 'cached ' : '', "patches from ").concat(patchesBaseUrl), hasSpin ? undefined : 2);
+ START = Date.now();
+ return [4 /*yield*/, axios_1["default"].get(patchesUrl)["catch"](function () {
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Downloading patches. Retrying.');
+ return axios_1["default"].get("".concat(ghUrl, "?v=").concat(Date.now()));
+ })];
+ case 1:
+ _a = _b.sent(), data = _a.data, headers = _a.headers;
+ END = Date.now();
+ if (!headers['etag']) {
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Generating patch hash');
+ headers['etag'] = crypto.createHash('md5').update(typeof data === 'string' ? data : JSON.stringify(data)).digest("hex").slice(-5);
+ }
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("Downloaded patches in ".concat((END - START) / 1000, "s"));
+ if (config === null || config === void 0 ? void 0 : config.cachedPatch) {
+ //save patches.json to current working directory
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Saving patches to current working directory');
+ (0, fs_1.writeFileSync)(patchFilePath, JSON.stringify(data, null, 2));
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed('Saved patches to current working directory');
+ }
+ return [2 /*return*/, resolve({
+ data: data,
+ tag: "".concat((headers.etag || '').replace(/"/g, '').slice(-5))
+ })];
+ }
+ });
+ }); }); };
+ if (!((config === null || config === void 0 ? void 0 : config.cachedPatch) && data)) return [3 /*break*/, 1];
+ queue.add(freshPatchFetchPromise);
+ return [2 /*return*/, { data: data, tag: "CACHED-".concat((crypto.createHash('md5').update(typeof data === 'string' ? data : JSON.stringify(data)).digest("hex").slice(-5)).replace(/"/g, '').slice(-5)) }];
+ case 1: return [4 /*yield*/, freshPatchFetchPromise()];
+ case 2: return [2 /*return*/, _a.sent()];
+ }
+ });
+ });
+}
+exports.getPatch = getPatch;
+/**
+ * @private
+ * @param page
+ * @param spinner
+ */
+function injectLivePatch(page, patch, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var data, tag;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ data = patch.data, tag = patch.tag;
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Installing patches');
+ return [4 /*yield*/, Promise.all(data.map(function (patch) { return page.evaluate("".concat(patch)); }))];
+ case 1:
+ _a.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("Patches Installed: ".concat(tag));
+ return [2 /*return*/, tag];
+ }
+ });
+ });
+}
+exports.injectLivePatch = injectLivePatch;
+/**
+ * @private
+ */
+function getAndInjectLivePatch(page, spinner, preloadedPatch, config, sessionInfo) {
+ return __awaiter(this, void 0, void 0, function () {
+ var patch, patch_hash;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ patch = preloadedPatch;
+ if (!!patch) return [3 /*break*/, 2];
+ return [4 /*yield*/, getPatch(config, spinner, sessionInfo)];
+ case 1:
+ patch = _a.sent();
+ _a.label = 2;
+ case 2: return [4 /*yield*/, injectLivePatch(page, patch, spinner)];
+ case 3:
+ patch_hash = _a.sent();
+ sessionInfo.PATCH_HASH = patch_hash;
+ return [2 /*return*/];
+ }
+ });
+ });
+}
+exports.getAndInjectLivePatch = getAndInjectLivePatch;
+/**
+ * @private
+ */
+function getLicense(config, me, debugInfo, spinner) {
+ return __awaiter(this, void 0, void 0, function () {
+ var hasSpin, _a, START, data, END, error_1;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (!(config === null || config === void 0 ? void 0 : config.licenseKey) || !(me === null || me === void 0 ? void 0 : me._serialized))
+ return [2 /*return*/, false];
+ hasSpin = !!spinner;
+ if (!spinner)
+ spinner = new events_1.Spin(config.sessionId || "session", "FETCH_LICENSE", config.disableSpins, true);
+ if (!(typeof config.licenseKey === "function")) return [3 /*break*/, 2];
+ //run the funciton to get the key
+ _a = config;
+ return [4 /*yield*/, config.licenseKey(config.sessionId, me._serialized)];
+ case 1:
+ //run the funciton to get the key
+ _a.licenseKey = _b.sent();
+ _b.label = 2;
+ case 2:
+ if (config.licenseKey && typeof config.licenseKey === "object") {
+ //attempt to get the key from the object
+ //@ts-ignore
+ config.licenseKey = config.licenseKey[me._serialized] || config.licenseKey[config.sessionId];
+ }
+ //asume by now the key is a string
+ spinner === null || spinner === void 0 ? void 0 : spinner.start("Fetching License: ".concat(Array.isArray(config.licenseKey) ? config.licenseKey : typeof config.licenseKey === "string" ? config.licenseKey.indexOf("-") == -1 ? config.licenseKey.slice(-4) : config.licenseKey.split("-").slice(-1)[0] : config.licenseKey), hasSpin ? undefined : 2);
+ _b.label = 3;
+ case 3:
+ _b.trys.push([3, 5, , 6]);
+ START = Date.now();
+ return [4 /*yield*/, axios_1["default"].post(initializer_1.pkg.licenseCheckUrl, __assign({ key: config.licenseKey, number: me._serialized }, debugInfo))];
+ case 4:
+ data = (_b.sent()).data;
+ END = Date.now();
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("Downloaded License in ".concat((END - START) / 1000, "s"));
+ return [2 /*return*/, data];
+ case 5:
+ error_1 = _b.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail("License request failed: ".concat(error_1.statusCode || error_1.status || error_1.code, " ").concat(error_1.message));
+ return [2 /*return*/, false];
+ case 6: return [2 /*return*/];
+ }
+ });
+ });
+}
+exports.getLicense = getLicense;
+function earlyInjectionCheck(page) {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ //@ts-ignore
+ return [4 /*yield*/, page.waitForFunction("require(\"__debug\").modulesMap[\"WAWebCollections\"] ? true : false", { timeout: 10, polling: 500 })["catch"](function () { })
+ //@ts-ignore
+ // return await page.evaluate(() => { if (window.webpackChunkwhatsapp_web_client) { window.webpackChunkbuild = window.webpackChunkwhatsapp_web_client; } else { (function () { const f = Object.entries(window).filter(([, o]) => o && o.push && (o.push != [].push)); if (f[0]) { window.webpackChunkbuild = window[f[0][0]]; } })(); } return (typeof window.webpackChunkbuild !== "undefined"); });
+ ];
+ case 1:
+ //@ts-ignore
+ _a.sent();
+ //@ts-ignore
+ // return await page.evaluate(() => { if (window.webpackChunkwhatsapp_web_client) { window.webpackChunkbuild = window.webpackChunkwhatsapp_web_client; } else { (function () { const f = Object.entries(window).filter(([, o]) => o && o.push && (o.push != [].push)); if (f[0]) { window.webpackChunkbuild = window[f[0][0]]; } })(); } return (typeof window.webpackChunkbuild !== "undefined"); });
+ return [2 /*return*/, true];
+ }
+ });
+ });
+}
+exports.earlyInjectionCheck = earlyInjectionCheck;
+function getAndInjectLicense(page, config, me, debugInfo, spinner, preloadedLicense) {
+ return __awaiter(this, void 0, void 0, function () {
+ var l_err, data, l_success, keyType, error_2;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!(config === null || config === void 0 ? void 0 : config.licenseKey) || !(me === null || me === void 0 ? void 0 : me._serialized))
+ return [2 /*return*/, false];
+ data = preloadedLicense;
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Checking License');
+ _a.label = 1;
+ case 1:
+ _a.trys.push([1, 11, , 12]);
+ if (!!data) return [3 /*break*/, 3];
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Fethcing License...');
+ return [4 /*yield*/, getLicense(config, me, debugInfo, spinner)];
+ case 2:
+ data = _a.sent();
+ _a.label = 3;
+ case 3:
+ if (!data) return [3 /*break*/, 9];
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('Injecting License...');
+ return [4 /*yield*/, page.evaluate(function (data) { return eval(data); }, data)];
+ case 4:
+ l_success = _a.sent();
+ if (!!l_success) return [3 /*break*/, 6];
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('License injection failed. Getting error..');
+ return [4 /*yield*/, page.evaluate('window.launchError')];
+ case 5:
+ l_err = _a.sent();
+ return [3 /*break*/, 8];
+ case 6:
+ spinner === null || spinner === void 0 ? void 0 : spinner.info('License injected successfully..');
+ return [4 /*yield*/, page.evaluate('window.KEYTYPE || false')];
+ case 7:
+ keyType = _a.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.succeed("License Valid".concat(keyType ? ": ".concat(keyType) : ''));
+ return [2 /*return*/, true];
+ case 8: return [3 /*break*/, 10];
+ case 9:
+ l_err = "The key is invalid";
+ _a.label = 10;
+ case 10:
+ if (l_err) {
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail("License issue".concat(l_err ? ": ".concat(l_err) : ""));
+ }
+ return [2 /*return*/, false];
+ case 11:
+ error_2 = _a.sent();
+ spinner === null || spinner === void 0 ? void 0 : spinner.fail("License request failed: ".concat(error_2.statusCode || error_2.status || error_2.code, " ").concat(error_2.message));
+ return [2 /*return*/, false];
+ case 12: return [2 /*return*/];
+ }
+ });
+ });
+}
+exports.getAndInjectLicense = getAndInjectLicense;
+// export * from './init_patch';
diff --git a/src/controllers/popup/index.js b/src/controllers/popup/index.js
new file mode 100644
index 0000000000..47266b2a44
--- /dev/null
+++ b/src/controllers/popup/index.js
@@ -0,0 +1,188 @@
+"use strict";
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.closeHttp = exports.popup = void 0;
+/** @internal */ /** */
+var events_1 = require("../events");
+var socket_io_1 = require("socket.io");
+var os_name_1 = require("os-name");
+var open_1 = require("open");
+var get_port_1 = require("get-port");
+var command_exists_1 = require("command-exists");
+var http_1 = require("http");
+var tools_1 = require("../../utils/tools");
+var logging_1 = require("../../logging/logging");
+var placeholders = {
+ "authenticated": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAUwklEQVR4nO3df6xf9V3H8Sdf7u662nU3TdNgU5ubujRN4ypW0jSIs+sIQ7LUbvyYDDYYvzYRYQwZaXDZCEGcSFA2N35UZR3rsCIyRKyVYcePyaRpWIcMm4K11tpUUhG72t3dffGP1z3rbbk/vj/OOe/POef1SJobaPv9vnvv9/v6vj/nfH6cwOcws4ZqRRdgZnEcAGYN5gAwazAHgFmDDUQXYFZjJwEnj32dDYwCrwG7gR3AobDKxjgAzPI1G/gwcBFwCpO/x0aA54GvABtRMJTuBN8GNMvFIHAVcAMwr8u/+ypwC3AXcCTnuqbkawBm/RsA7gBuo/s3P8Dcsb//TWB5jnVNywFg1r8rgcvo//10KvAUcDMwq9+iOuEAMOvPauAmNATIw0zgd1AQrMzpMSd1IquKfgqz2loIfA34mQIe+yTgQuBE4DngRwU8hzsAsx7NQGP+pQU/x03o2sCyIp7AAWDWmyuAD5b0XCuBZ4CryW+oATgAzHqxAriRcufRzAL+CPgGsCivB3UAmHVnDnoj9nK7Lw9nAv+IJhv1HUAOALPOtdAn/4rgOuYBX0WzCPsKIgeAWefWoLF/Cu+bFuoCnkG3InuqybcBzTqzCNiAbs+lZA5wPro4+B26vF2YQpKZpW4GcCvwzuhCJjGIJg/9HbCkm7/oADCb3iXA2ugiOnAamkHY8QVCDwHMprYcuBt4R3QhHZoJfAD4aeBpplld6A7AbHJDaLZfauP+6bTQxcpvog1JpvyDZvZmLeBaqHSPvBz4B6YYEjgAzCa2Ck29rfp7ZAjNGfg8Gh4co+r/OLMinIRa/6HoQnLSAj4FfJ3j9hlwAJgdawDN9it1Z56SrEHTmH8yHHAAmB1rLbrtV1eXoGsCgAPAbLxFaHPON42Va+YWtHuxA8BszAy0F9/i6EJKsAC4GBwAZpkLKW+DjxRcBA4AM9C2Xp9BXUBTLAcWOACs6WahhT4LowsJcIoDwJqsha6KnxVdSJBhB4A12XJ0lFdTz8ic7QCwphpCrf/86EICjTgArIla6Div1dGFBNvvALAmOg24Dt8Fe77p3wBrnnloZdyc6EKCvQrscABYkwwA1xO/rXcKHgLaDgBrkjNIZ1vvSCNom7PGfyOsORaguf6zowtJwAPAdnAAWDMMovv9dVzj36296HsBOACsGd5Pvdf4d2oU3f3Yn/0PB4DV3SLU+td9jX8nHhz79RMOAKuzGWiV39LoQhKwD336t8f/TweA1dl5wK9HF5GAUXT7c9/xv+EAsLpaAnyWZq3xn8xDwKaJfsMBYHU0C437F0UXkoA9qPUfneg3HQBWNy20vdea6EISkLX+eyf7Aw4Aq5tlaF//wehCErAJtf+TcgBYncxGW14viC4kAXvQhJ8JW/+MA8DqIjsR94zoQhIwbeufcQBYXayg2dt7jfcA07T+GQeA1cEc1PrPjS4kAXuAdUzT+mccAFZ1A6j1XxVcRwo6bv0zDgCrupXoRe/Xchetf8bfNKuyuaj1b/r2XgC76aL1zzgArKoGgGvQBp9NN4Jm+3Xc+mccAFZVq4Cr8WsY4H7gkV7+or95VkXz0aEe3t4LdqElz121/hkHgFVNtr3XKdGFJCBr/d+0zLdTDgCrmjPx9l6Z+4DH+nkAB4BVyUJ01X9WdCEJeBHtd9BT659xAFhVDKJVfj8XXUgCjqC5D/un+4PTcQBYVaxB6/wN1gNb8nggB4BVgXf2PWoH+l701fpnHACWuhlorLskupAEHEat/4G8HtABYKk7B+3ua3AP8ESeD+gAsJQtBm7CO/uCzvK7lZxa/4wDwFI1E+/sm8m99c84ACxFLXSgx9roQhLxReDJIh7YAWApyg718M6+av1vI+fWP+MAsNTMRLP9FkYXkoBDqPV/tagncABYSlrAxeg4b4M7Kaj1zzgALCVL0XRf7+wLzwJ3UFDrn3EAWCpmA59Ha/2b7nW0zLew1j/jALAUtNASXx/qIbejDqBwDgBLwcloQ0u3/hrz3wm0y3gyB4BFy1r/edGFJOAguur/WllP6ACwSC3gSmB1dCEJaKMg3FbmkzoALJIP9TjqCbTYp5TWP+NvvEUZQp94PtRDV/tLbf0zDgCLMID29D81upAEtNGKxx0RT+4AsAgr0X1uv/60q+99lNz6Z/wDsLLNQYtbfKiHNvVch+b8h3AAWJkG0Fh3RXQhCRhFKx5fjCzCAWBlOg24Cr/uQGf53U9Q65/xD8LKMg9NcfWhHrAHLXo6HF2IA8DKMIDO81seXUgCRtFhnjujCwEHgJXjdDTjz2DT2K/Q1j/jALCinYSu+ntnX3gFffofiS4k4wCwIg2iF7zP89NR3jeiEEiGA8CKdBZwWXQRidgIPBxdxPEcAFaUBWiuv3f21QW/m0io9c84AKwIg+gFvzi6kAQcQbP9dgfXMSEHgBVhLT7KO7MBzfdPkgPA8rYQt/6ZF9HxZsm1/hkHgOVpEB1gORxcRwoOo9Z/b3QhU3EAWJ7Ow0d5Z/4U2BxdxHQcAJaXRejT3zv7anOPW9G9/6Q5ACwPg2i234LoQhJwCK172BddSCccAJaHjwJrootIxD3A49FFdMoBYP1ajE7zdeuvLb0LO8q7CA4A68cgOsDSh3qo9V+HtvmqDAeA9eMy4MzoIhJxJ7A1uohuOQCsV0vRJBe/hko6yrsI/uFZL2YAX8CHeoAO87iBEo7yLoIDwLrVQht7rgquIxV3AE9HF9ErB4B1ayna5MOvHY35v0gi23v1wj9E68ZM4G58qAfoKO91Y18rywFgnWqh8/xWRheSgOwo73+KLqRfDgDr1HLc+mdCjvIugn+Y1omZwJfHvjbdAXTVv/SjvIvgALDptNBY95ToQhLQRqv8tkcXkhcHgE1nBfDb0UUkYjOwPrqIPDkAbCqzgHvxoR6g5b2hR3kXwQFgk2nhQz0yo2ja847oQvLmALDJnAZ8KrqIRDyCdvetHQeATWQITfjxGn9t6pnEUd5FcADYRG4GlkQXkYBR9OZ/KbqQokQl/Czg5LFfvzD236Bpld9HY61nSXg/9RrzUd5HZUd511bZATAfuB6dGjM0xfOPop1VNqIJKLvLKM4YQlf93RkmeJR3Ecr6QbeADwLPAZ8E5jJ1+AygHWY/DTyDPpWseLfhQz0g0aO8i1BGAGQHRf456gC6NR/4C7TzrBXn/cAl0UUkYiPwUHQRZTix4G0dsjnkV9Nf2MwA3ocmYXwnh7rsWHOBvwbeEV1IAnaiIKz0Mt9OFRkAQ8ADwLk5Pd5bgPeiIHkKeCOnxzWN+38puogEHAE+QYM+ZIoaAswB/hI4K+fHHUQXZnz6bH7OAT4cXUQiNgCPRhdRpiI6gLnom/jLuT+ytNCmFHPRCSw/Luh5mmAu8DfAT0UXkoAXUOv/enQhZcq7A5iHxpJF7xrTQq3aH+M16v24Fx/qAbq2dD0VO9QjD3kGwALg7ylvy6gWSmxvVNGbC4G10UUkYj2wJbqICHkNARahtv9duTxa504Ye87F6Af4w5Kfv6oWAN8A3hZdSAK2AR8HfhBdSIQ8ZgIuAv4WvQkjtIDzxr5eSsPGcD1ooa7Jh3pU/FCPPPQ7BIh+8493DvA1dGHLJncZ+d+dqaq7qOB5fnnqJwBSevNn3g98BYfAZBahW6ie6w/fRlOfK7+zbz96fSGk+ObPnAV8HTgpupDEtNBV/6HoQhJwELX+jZjtN5VeAiDlN3/mdBQCvaw9qKsr8Xl+oE/8Sp/nl6duA6AKb/7MKhQCC4PrSMES4Bbc+oPG/HdGF5GKbl4QC6jOmz/zbrSScDi4jkiD+Dy/zAG0s6/vFI3pNAAWoCmjVXrzZ1agpciLogsJ8km0wWfTjaKLfpU/zy9PnQRA9uZfVnAtRVoB/BXVDLB+LMPn+WUeR7f9bJzpXhjz0IyxKr/5M8vQcKApm13OQFf9Z033Bxuglod65GGqABhCn5rLS6qlDMvQcKAJIfBpfJ4fqPW/BXg+upAUTRYAs9B6/lNLrKUsy9C/7eToQgp0CrrP7dZfa1Tuiy4iVRO9QAaBPwFWl1xLmZaiTqCOITALXfX3CknYQ40P9cjD8QHQQscfnxdQS9kWU88QWEf9/k29GEGb0b4YXUjKjg+AK9Bto6aoWwi8G/383PprV9+N0UWkbvwLZQVwO8178WQhsCK6kD7NxjskZRpxqEcesjf7ILpl1NQXTxYCVb3o2ULt7tLoQhJwGL35d0UXUgVZAFxIPe7192MY7SdQ1pZmeVqN9khsWvd2vDbw4Ngv60D2gvlYaBXpGEYLiKoUAnOAL6CJP023E336j0QXUhUt1PZ7wshRw1QnBFroKO+mTXGeyCH05t8TXUiVtNCb358exxqmGiFwFtoZ2a2/rvg/HF1I1bRo9lLZqQyjEEj1wuA8dNfG4a1DPW5C036tC03/5JjOMLowmFoIZBO23hldSAJeR7P99kUXUkUOgOkNk14IrEV3bpr+82ujef6PBddRWS1gd3QRFTBMOiGwALX+PhwVtqOVfo3e2bcfLfRN9NhpesPEh8AA2tbb+xxqR991aJsv61EL3T7xNkmdGSY2BM5DB6C49Yd7gCeiC6m67IX05dAqqmWYmBBYhC78ufWHZ/GhHrnIAuAhYG9kIRUzTLkhMIhb/8yrqPVv/KEeecgC4DC6leIplJ0bprwQ+CiwpoTnSd0omvb8ZHQhdTH+ePDvAm9FW0ifEFRP1QwB7wWeA/69oOdYjG51+UgvHepxHV7mm5vxAQDwLXSm3nIcAp3KQuB7wL8Bb+T42DPQxS6v1YD96GTjl6MLqZPjrya3gWuBTQG1VNkw8FW0LDfPK/SXAGfm+HhVNYLmPvhuVc4merEeAS7Hs6u6NR8dTZ5XCGSHeviqvw/1KMxkL9RDwEdwCHQrrxCYga76+4hz3Z3yoR4FmepFehCFwNZySqmNPELgSnTEedONoCDcEV1IXU33Aj0IfAjfdulWPyGwHB3qMZB3URX0GLA+uog66+TFeQC4AF+A6VYvITALzXCbV1RRFbIbtf6+5VegTl+Ye1En4FasO92GwFVob/+mO4K2OnspupC666Y93Q2cjX8o3eo0BFYC1+PWv42mpt8fXUgTdDs+3QWci3Zftc5NFwKzUes/p8yiEpUd6uFp6SXo5Sr1C8D5eCORbk0WAi30yZ/6BqRlyA71eCW6kKbo9TbVdnRNwFswd2eiEDgNjf3d+sMD+FCPUh2/FqAb/wFsQ1NV355TPU3wdnSP/7toQ8v7gJ+NLCgR3wcuBV6LLqRJ+gkAUAewHTgDh0A3shD4eeA9eIefQ6gL8q3mkuXxwtuK5gl4W+buzEfbe7n1hw3AI9GFNFFenzxbcQhYb3agQz28vVeAPFvPrSgE9uf4mFZvr+OdfUPlPfbcik4a9g/UptNG8/y3RBfSZEVcfNqCQ8Cmtw0f6hGuiABoA5txCNjkDqIVj97ZN1hRt5+yEPg4/iHbsUaBL+F9JpJQ5P3nNrq14xCw8bJDPSwBRU9AyVZ2OQQMdKjH9ejqvyWgjBloDgEDtf63ow7AElHWFFSHgG0F7owuwo5V5jTULAQA7sUn3TTJftT6H44uxI5V9iKULASuwePAphhBpxo/H12IvVnEKrQ22u7JIdAMW9DxZpagfpcD9+oNtAjkP9HmGG8NqcKKthedLeH1IYmKXIfuTqDeRoDP4k1kkxa9EcUoDoE6agMP4519kxc1BBivjTYa3YeHA3Xxr8BFaOKPJSy6A8i4E6iPI8CNeGffSkglAMAhUAfe2bdiUhgCjOfhQLX9C2r9/ye6EOtMagEADoGqOgx8Am30YRWR0hBgPA8HqqWNzjd4NLgO61KKHUDGnUB1fA/tAPWD6EKsOykHADgEquAQcDHwz8F1WA9SHQKM5+FAutp4e69KS70DyLgTSNNzwOXo3r9VUFUCABwCqXkNuBB4OboQ610VhgDjeTiQhjbe3qsWqtQBZNwJxHsKneb7o+hCrD9VDABwCEQ6CHwIrfW3iqvaEGC8bDjwW2g8asVrAzejzVysBqocAHA0BC7Huw2XYQtwV3QRlp+qBwB4y/Gy7EcXX33Lr0bqEADgECjaKFrjvzO6EMtXXQIAHAJFehjYEF2E5a+qdwEm8wbahPJl4HTgbbHl1MJedNX/v6MLsfzVqQPIuBPIzyhwHbAnuhArRh0DABwCednI0ePcrIbqNgQYz8OB/ryCWv//jS7EilPXDiDjTqA3I2iC1YHoQqxYdQ8AcAj04h5gc3QRVrw6DwHG83Cgcy8BFwD/F12IFa8JHUDGncD0jqDvj9dWNESTAgCOhsBFeHw7kT8EnowuwsrTtAAAhcBjOASO9zxa6WcN0sQAAIXAFhwCmcNoReXh6EKsXE0NAHAIjHcrPtGnkZocAOAQAPg28AfRRViMpgcANDsEXsfbejeaA0CaGgLrgBeji7A4DoCjshD4CM3Y8PIxNOPPGswBcKwsBC6g3iFwAPhNtNzXGswBMLEnqW8ItIFrgd3BdVgCHACTq2sIbAIeiC7C0uAAmNqTwPnU59NyL/r0b0cXYmlwAEzvaeBctEFGlbXRuH9/dCGWDgdAZ7ah3XGqvC32euDR6CIsLQ6Azm0Dzqaa9813Ajfg1t+O4wDozguoE6hSCIzgNf42CQdA914Afg0tn60Cr/G3STkAerMLXRjcHl3INLahNf5u/W1CDoDe7QI+gFbTpegQcOnYV7MJOQD6swddE9gaXMdEbgR2RBdhaXMA9G8vmiyU0jbam4G7oouw9DkA8rEfTRveRPx4+wDwG+jqv9mUHAD5OYjG3OuJW2XXBq6hPlOXrWBNORikLCNoOfGJwMqxr2X6M+D3ie9CrCIcAPn7MfAt4L+A1cBbSnreB4ErgB+W9HxWAx4CFKONLsKdDewr+LlGgd9F1yC8rbd1xQFQrM3Ae9BtwiLa8l3ArwKfwRf9rAcOgOLtBN4HfAy9YfNwGPg94BeBx/GY33rkACjHCLABeBeaOPQEvd0pODLucdahbb3NenYCn4suobHmA2cAvwKcDCwBZkzyZ3eiQ03vxrf4LEcOALMG8xDArMEcAGYN5gAwazAHgFmD/T8mPr0yWqP/ywAAAABJRU5ErkJggg==",
+ "loading": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAhOAAAITgBRZYxYAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABtCSURBVHja7d19kF11fcdxd0kIiYnRISQIiIABUjBSE8oQEOIUUURtKBqeFFokaqA6oExpBzBjBitghU5bichTQTKoVSwQBMHiDIJg1FoIBWVQYYgGCAKxYEhCdtPv0bOddWVz7+7eh9/vnNcfrxlGycOc35nzebN7995XbNmy5RUAQL24CAAgAAAAAQAACAAAQAAAAAIAABAAAIAAAAAEAAAgAAAAAQC0xSEfe3pC2C3MC+8tzSv/twmuEQgAoBqDPyUcE64La8OWBtaW/27xa6a4hiAAgLyG/8jwzbChidEfzoby9zjSNQUBAKQ9/HPDd8Yw+sMpfs+5rjEIACCt4Z9efum+vw3jP6C//DOmu+YgAIDuj/+c8Hgbh3+o4s+a49qDAAC6N/7Hh/UdHP8BxZ95vDMAAQB0fvzP6cLwD3WOswABAHRu/BcmMP4DjnUmIACA9o//fuGFhAJgvdcEgAAA2jv+24dHExr/AavDDGcEAgBoTwBcnuD4D/iSMwIBALR+/PcJmxMOgL7wJmcFAgBobQCsSHj8B9zirEAAAK0b//kZjP+A+c4MBADQmgBYnlEALHdmIACAsY//+PBcRgFQ/F3HOzsQAMDYAuDwjMZ/wOHODgQAMLYAWJZhACxzdiAAgLEFwIMZBsCDzg4EADC2AFiXYQCsc3YgAIDRj/+kDMd/wCRnCAIAGF0AzMw4AGY6QxAAwOgC4JCMA+AQZwgCABhdAByRcQAc4QxBAAACABAAgAAABAAgAAABAAgAQACAABAAgAAAASAAAAEAAkAAAAIABIAAAAQACAABAAgAEAACABAAIAAEAAgAQAAIABAAgAAQACAAAAEgAEAAAAJAAIAAAASAAAABAAgAAQACABAAgAAABAAgAAABAAgAQAAAAgAEgAAABAAIAAEACAAQAAIAEAAgAAQAIABAAAgAQACAABAAgAAAASAAQAAAAkAAgAAABIAAAAEACAABAAIAEAACAAQAIAAEAAgAQAAIABAAgAAABAAgAAABAAgAQAAAAgAQACAABAAgAEAACABAAIAAEACAAAABIAAAAQACQAAAAqCbjv7MpnFhp7BPmBF6XRcEAPzen520sjfMCPuEncI410UA5Dj2U8PCcE14IDwd+sOWQTaHJ8N94dLw7jDR9UMAUIOxnxjeHS4N94Unw+awZZD+8HR4IFwTFoaprp8ASPW/8BeFO8KmIWPfrPXh5jIeelxXBAAVGv2ecsRvDuuHjH2zNoU7wiJfIRAAKQx/b3h/+NkoR384Pw5HusYIACow/keGH49y9Ifzs/D+4lsHrrEA6Mb4HxZWtXj4h7o7zHG9BYAAIMPhnxPubvHwD7UqHOZ6C4BOjv+Zoa/N4z/4WwMnuO4CQACQ0fifMIYv9Y9UXzjTdRcA7R7+CeWL+7Z0wfl+ekAACAAyeDX/+R0a/qGKFwtOcA4CoB3j/5pwb5fGf8BNYZLzEAACgATHf1K4qUvjP+De8BrnIQBaOf7bhNu7PP4D7hABAkAAkOD439Hl8R9we9jGuQiAVgXARYmMvwgQAAIA49/YRc5GALRi/E9MbPxFgAAQABj/xk50RgJgLOO/R3gx0QAQAQJAAGD8h/di2MNZCYDRBsDyhMdfBAgAAYDx37rlzksAjGb8Z3fwZ/1FAAIA49+e9wiY7dwEwEgD4KZMxl8ECAABgPEf3k3OTgCMZPz3zWz8RYAAEAAY/+Ht6wwFQLMBcHamASACBIAAwPj/sbOdowBoNgDuyTgARIAAEAAY/z90j7MUAM2M/7SMXvwnAgSAAMD4N/diwGnOVAA0CoATKjD+IkAACACM/x/yqaoCoGEALKlQAIgAASAAMP6/t8TZCoBGAfCFigWACBAAAoC6j3/hC85XADQKgBsqGAAiQAAIAOo8/oUbnLEAaBQAKysaACJAAAgA6jr+hZXOWQA0CoAfVTgARIAAEADUcfwLP3LWAqBRAKyoeACIAAEgAKjb+BdWOG8B0CgALqtBAIgAASAAqNP4Fy5z5gKgUQAsrUkAiAABIACMf13Gv7DUuQuARgFwSo0CQAQIAAFg/OviFGcvABoFwK41CwARIAAEgPGvg12dvwBoJgJWiQAEgAAw/pWxyvkLgGYD4DM1DAARIAAEgPGvqs+4BwRAswEwr6YBIAIEgAAw/lU0z30gAEYSAfeIAPeBABAAxj9797oPBMBIA+CtNQ4AESAABIDxr4q3uhcEwGgi4DYRIAIEgAAw/tm6zb0gAEYbAHNDvwgQAQJAABj/7PSHue4HATCWCFhS8wAQAQJAABj/HC1xPwiAsQZAT7heBIgAASAAjH82rg897gkB0IoIeGVN3xxIBAgAAWD8s3vTn/BK94QAaGUE7B6eEAEiQAAIAOOfrCfC7u4JAdCOCJglAkSAABAAxj/Z8Z/lnhAAIkAECAABYPyNPwJABIgAASAAjL/xRwCIABEgAASA8Tf+AsBFEAEiQAAIAONv/AUAIkAECAABYPyNvwBABIgAASAAjL/xFwAMHwF7iwARIAAEgPFv+/jv7Z4QACJABNQhAA7OOAAOdobG3/gLgLpFwBoRIAJaFAC7ZxwA3pHN+LfKGuMvAESACKhbAGyXcQBs5wyNv/EXACJABLgvRh8Bz2Y4/s86O+Nv/AWACBABImBsAbAqwwBY5eyMv/EXACJABIiAsQXAxRkGwMXOzvgbfwEgAkSACBhbAByaYQAc6uyMv/EXAIgAETC2AOgNazMa/+Lv2uvsjL/xFwCIABEw9gi4MqMAuNKZGX/jLwAQASKgNQEwN/RnMP7F33F/Z2b8jb8AQASIgNZFwHUZBMBXnZXxN/4CABEgAlr/roAbEx7/TWGmszL+xl8AIAJEQL1+JPASZ2T8jb8AQASIgPYEwOTwQILj/5Mw1RkZf+MvABhZBOwlAkTACCJgj/BMQuP/XNjT2Rj/EYz/Xu4JAYAIEAGji4C3hc0JjH/xd3i7MzH+xl8AIAJEQOci4MNdjoDiz/6wszD+xl8AIAJEQOcj4O3ll+C78WV//+Vv/I2/AEAEiIAuRsCe5YvwOvmCP9/zN/7GXwDQxgj4lQgQAU1GwNTix/DKn8Vv58/5X+LV/sZ/BH5l/AUAIkAEdCYEZhbvxtfitw3uL39Pb/Jj/I2/AEAEiIDEQ2D/8gOE1o7xU/2u9N7+xt/4CwBEgAjILwSKjxI+tHwHwVXh2a0M/rPlv3Nx+Wt8pK/xN/4CABEgAioUBduVnytwcKn45+1cG+Nv/AUAIiB13w7j3BMkMv7jwreNv/EXAIiAzvi8+4FEAuDzxt/4CwBEQGctcj/Q5fFfZPyNvwBABHTexnCQ+4Eujf9BYaPxN/4CABHQHY+Fbd0PdHj8tw2PGX/jLwDoZgTsKQI2ne5eoMMBcLrxX+ntoAUAIqDr1obJ7gU6NP6Tw1rj714QAIiANCxxH9ChAFhi/N0HAgARkI5nwjbuA9o8/tuEZ4w/AgARkJZD3QO0OQAONf4IAERAej7r/GlzAHzW+CMAEAHpecjZ0+YAeMj4IwAQAWna2dnTpvHf2fgjAMgtAn5ZowA40LnTpgA4sEbj/0vjLwAQAblZ4MxpUwAsMP4IAERAuj7ivGlTAHzE+CMAEAHeEAhvAGT8EQCIgISc7ZxpUwCcbfwRAIiAdH3QGdOmAPig8UcAIALS9S7nS5sC4F3GHwFAVSJgZgUjYK6zpU0BMLeC4z/T2QoAREAVxr8vbO9caVMAbB/6jD8CABGQnu87T9ocAd83/ggAREB6znWWtDkAzjX+CABEQHr2c460OQD2M/4IAERAWh51fnQoAh41/ggAREA6PuTs6FAAfMj4IwAQAWl4OIxzbnQoAMaFh40/AgAR0H0LnRcdjoCFxh8BgAjorpWhx1nR4QDoCSuNPwIAEdAdT4fdnBFdioDdwtPGHwFAHSJgdULjvynMdzZ0OQLmh00Jjf9q448AoOoRsNiZkEgELDb+CADqEAFvCD/t8vgvdRYkFgFLuzz+Pw1vcBYIANodAVPDrV0Y/vXhOGdAohFwXFjfhfG/NUx1BggAOhUBveFzHRz/1T7qlwwiYG75pfhOjf/nQq9rjwCgGyHwvvDzNn/E77VhR9ebTCJgx3Btmz86+Ofhfa43AoBuR8D4cFpY0+LxvzHMdo3JNARmhxtbPPxrwmlhvGuMACClEJgUzgh3h82jHP214eowzzWlIiEwL1wd1o5y9DeHu8MZYZJrigAg9RjYPnwgfDncXw57/5CxfzH8ItwVPh0OLF5b4PpR0RDoDQeGT4e7wi/Ci0PGvr8MhfvDl8MHwvauHwKAKnyr4HVhVni1awK/C4NXh1nhdb60jwAAAAQAACAAAAABAAAIAABAAAAAAgAAEAAAIAAAAAEAAAgAAEAAAAACAAAQAACAAAAABAAAIAAAAAEAAAgAAEAAAAACAAAQAACAAAAABAAAIAAAQAAAAAIAABAAAIAAAAAEAAAgAAAAAQAACAAAQAAAAAIAABAAAIAAAAAEAAAgAAAAAQAACAAAQAAAgAAAAAQAACAAAAABAAAIAABAAAAAAgAAEAAAgAAAAPIOgDOveWmXMD8cFz4R/jFcFa6moy4L54VTw1HhgDDZzQ40a/ZRX54cDggLwqnhvHB5uJqOuip8LnwiHBfmh126HgAxKj1h/3JsVoUtJGtDuCUsDjt5wAEvM/o7hcXhlrAhbCFZq8oo2z/0dCwAYkDGh4+G1YY1S/3hznCwhx4QA3JwuDP0G9YsrQ4fDePbGgAxGgvDI0a0Mr4R9vYQhFoO/97hGwa0Mh4JC1seADESrw/3GsxKeilcEHo9FKEWw98bLggvGc1Kuje8viUBUHypODxlKCuveI3AVA9IqPT4Ty2/x28oq+2p4ls7YwqAGISTw0bjWBsPhZkelFDJ8Z8ZHjKOtbExnDyqAIghOMMg1tKvRQBUcvx/bRRr6YwRBUAMwDvDZmNY668E+HYAVOfL/v7Lv742h3c2FQDx4J8V1hlBrwnwwkCoxAv+fM+fdWHWVgOgeMc4P+bHIBd4iELWAXCB8WPQjwlO3loAfMroMeRHBL1PAOT7c/5+1I/BPvWyARAP+hnheaPH0DcL8jCFLAPAm/ww1PNhxssFwCXGjmF422DI7+19DR4v55I/CIB4wO8aNhk6hnGnhypkFQB3GjqGsSnsOjgAzjRyNPgAIZ8iCPl8qp8P9mFrzhwcAHcZORpY7OEKWQTAYgNHA3f9LgDiwT499Bk4Gr0vgIcrZBEAfu6fRvrC9CIAFhk3mrCheJ8ID1hIevwnhw0GjiYsKgJgmXGjSQd4yELSAXCAYaNJy4oAuMGw0aQFHrKQdAAsMGw06YYiAFYaNpp0qocsJB0Apxo2mrSyCIDVho0mnechC0kHwHmGjSatLgJgo2GjSV/0kIWkA+CLho0mbSwC4CnDRpMu9JCFpAPgQsNGk54qAuA+w0aTTveQhaQD4HTDRpPuKwLgFsNGk47xkIWkA+AYw0aTbikC4ArDRpPe4iELSQfAWwwbTbqiCICzDBtNfiDQDA9ZSDoAZvggIJp0VhEAs4wbTfiBByxkEQE/MG40YdbApwH+xMDRwLkerpBFAJxj3Gjgp4M/Dvh8A0cDsz1cIYsAmG3gaOD8wQGwv4FjKx7xYIWsIuARI8dW7P//AVBGwLcMHcM4yUMVsgqAk4wcw/jWwH0yOADeXL7S2+Ax2P2h10MVsgqA3nC/sWOI4idE3vxHAVBGwHUGjyGO8ECFLCPgCIPHENcNvkeGBsAeYb3Ro3SbBylkHQG3GT1K68MewwZAGQHH+FYA4dGwg4coZB0AO4RHjR/F20QPvT9e9qaJB/9SA1hrz/uxP6jUjwU+bwBrbenL3RvDBUBP+LohrO1b/i7w4IRKRcACbxFcW18PPU0HQBkBE8NXDGKtvBhO8MCESkbACeFFg1grXwkTh7snGt40MQif9JqAWlgTDvCghEpHwAFhjWGsxY/7fbLR/dDUTRPDcHR4wUhW1g/Dzh6QUIsI2Dn80EhW1gvh6GbuhaZvmhiIXcKVoc9gVsZT4bQwzoMRahUB48Jp4SmDWRl94cqwS7P3wYhvnBiLfcPNxjNrvw3nhSkehlDrEJgSzgu/NaBZuznsO9LzH/WNE+PxxuIjYssvH3uNQB6jf0M4OUzz8AMGhcC0cHK4QQxk8z3+4ts454Y3jvbcW3LzFN8/DovKjxW+OtweHihfWPYkHfV4GWU3hkvDkvCe4qc6POiAJmJgYnhPWBK+EG4sx2Z1eJKOKl6w+T/h9nB18TG+YVHxOo5WnLUbHgBqyEUAAAEAAAgAAEAAAAACAAAQAACAAAAABAAAIAAAAAEAAAgAAEAAAAACAAAQAACAAAAABAAAIAAAAAEAAAIAABAAAIAAAAAEAAAgAAAAAQAACAAAQAAAAAIAABAAAIAAAAAEAAAgAAAAAQAACAAAQAAAAAIAAAQAACAAAAABAAAIAABAAAAAAgAAEAAAgAAAAAQAACAAAAABAAAIAABAAAAAAgAAEAAAgAAAAAQA7Xf3w33Tw1+FC8OXwrfDg+Gx8P3wH2FZOCe8JWzjugEIAPIc/X3CueXA94UtI/BMWB6OC5NcTwABQPrDv1f4augf4egPZ004NYx3fQEEAOkN/y7h8vBSi4Z/qJ+H94ce1xtAAJDG+L8zrGvT8A+1IrzKdQcQAHR3/M8axff4x+qhsKfrDyAA6PzwTwzXdXj4B3suvMNZAAgAOjf+k8IdXRz/ARvDQc4EQABQn/Ef8ETY2dkACADqM/4DfhC2c0YAAoD6jP+AK5wTgACgXuO/pXzjoT91XgACgPqM/4BbnRmAAKBe4z9gvrMDEADUa/wL33V+AAKAeo3/gNc6RwABQL3Gv3CKswQQANRr/AvXO08AAUC9xr/wv2G8cwUQANRn/Afs5WwBBAD1Gn8/DgggAKjh+BeOc8YAAoB6jX/h484ZQABQr/EvLHXWAAKAeo1/4W+cN4AAoF7jXzjamQMIAONfr/EvzHPuAALA+Ndr/Auvd/YAAsD412v8f+nsAQSA8a/X+Bcuc/4AAsD4189R7gEAAWD862VjmOw+ABAAxr9elrsPAASA8a+XTWEP9wKAADD+9XKJewFAABj/evlt2NH9ACAAjH+9fMz9ACAAjH+9/Jv7AUAAGP96uTdMcE8ACADjXx+/Cq91TwAIAONfH0+EWe4JAAFg/I0/AALA+Bt/AASA8Tf+AAgA42/8ARAAxt/4AwgAjL/xBxAAGH/jDyAAMP7GH0AAYPyNP4AAwPgbfwABgPE3/gACAONv/AEEgPE3/sYfQAAYf+MPgAAw/sYfAAFg/I0/AALA+Bt/AASA8Tf+AAgA42/8ARAAxt/4AyAAjL/xB0AAGH/jDyAAMP7GH0AAYPyNf6Xuz4nhz8NJ4e/CP4evlYp//vvy/yv+nYmuGQgAjL/xz/e+nB4+GG4M60dwjuvLX1P82umuJQgAjL/xz+OenBn+PfS14Fz7yt9rpmsLAgDjb/zTvB+nhX8Jm9pwxpvK33uaaw0CwPgbf+Ofzv14bPhNB867+DOOdc1BABh/42/8u3sv9oR/6MLZF39mjzMAAWD8jT+dvxenlC/W69Y9UPzZU5wFCADjb/zp3L24bfheAvfCPWGCMwEBUOUH7nbG3/gndD9eldA9cY0zAQFQ5Qfutcbf+CdyL56e4L3xt84GBEAVH7hnGn/jn8i9eEjYnOD9UbxfwGHOCARAlR64b0/0gWv86/mK//9K+D5ZFXqdFQiAKjxwZ4Rnjb/xT+R+PD6D++VEZwUCoAoP3H81/sY/oVf9/yKDe+ax4u/qzEAA5PzA3S1sNP7uhUTux8UZ3TuLnRkIgJwfuNcYf/dBQvfjnRndP3c6MxAAuT5sd2/RJ6kZf1pxP+6Q2QtRi7/rDs4OBECOD9wzjD8J3Y+nZHgvneLsQADk+MD9T+NPQvfjigzvpxXODgRAbg/bV7Xp89SNP6O9Jx/L8J56zNmBAMjtYfte409ib/6T40+jbPRxwSAAcnvgnm38Seh+nJbx/TXNGYIAyOmB+3njT0L34+yM77HZzhAEQE4P3OuNPwndj2/L+D57mzMEAZDTA/ce409C9+MRGd9rRzhDEAA5PXD/2/gjAAQACID6PXBvNf4IAAEAAqB+D9yrjD8CQACAAKjfA/fTxh8BIABAANTvgbvY+CMABAAIgPo9cPc2/ggAAQACoJ4P3UeMPwJAAIAAqN9D95+MPwJAAIAAqN9D9zDjjwAQACAA6vfQ7Q0PGn8EgAAAAVC/B+9fGn8EgAAAAVDPh+9K448AEAAgALwWwPgjAAQACICaPIAvNf4IAAEAAqB+D+Dx4bvGHwEgAEAA1O8hPD08bvwRAAIABED9HsRzwrouP1BXG38BIAAAAdD5h/Fe4Sddeph+L+zoHASAAAAEQHceyK8KKzr8IL0ibOv6CwABAAiA7j6Ui3cK/FTY0OYH6G/Cqa65ABAAgABI6+G8a7gqbG7xg7MIi4vCNNdZAAgAQACk+5D+k/C1FnxF4IXyy/2vc10RAIAAyOdh/crwF+WbBzX7Y4MPlx8/fLjv8yMAAAFQjYf3tDA7vCP8dfh4OLF8i+F9wmtcJwQAIAAAAQAIABAAAgAQACAABAAgAEAACABAAIAAEACAAAABIAAAAQACQAAAAgAEgAAAAQAIAAEAAgAQAAIABAAgAAQACABAAAgAEACAABAAIAAAASAAQAAAAgAQAIAAAAQAIAAAAQAIAEAAAAIAEAAgAAQAIABAAAgAQABAZQPgkIwD4BBnCAIAGF0AzMw4AGY6QxAAwOgCYFLGATDJGYIAAEYfAesyHP91zg4EADC2AHgwwwB40NmBAADGFgDLMgyAZc4OBAAwtgA4PMMAONzZgQAAxhYA48NzGY1/8Xcd7+xAAABjj4DlGQXAcmcGAgBoTQDMzygA5jszEABA6yJgRQbjf4uzAgEAtDYA9gmbEx7/vvAmZwUCAGh9BFyecAB8yRmBAADaEwDbh0cTHP/VYYYzAgEAtC8C9gsvJDT+68McZwMCAGh/BCxMKACOdSYgAIDORcA5CYz/Oc4CBADQ+Qg4vvwSfDe+7H+8MwABAHQvAuaExzs4/o/7nj8IACCNCJgergv9bRz+/vLPmO6agwAA0gqBueE7bRj/4vec6xqDAADSDoEjwzfDhjGM/oby9zjSNQUBAOQVAlPCMeWX7tc2Mfpry3+3+DVTXEMQAEA1gmBC2C3MC+8tzSv/twmuEQgAAEAAAAACAAAQAACAAAAABAAAIAAAAAEAAAgAAEAAAAACAABoif8DISN8hQwIU7wAAAAASUVORK5CYII="
+}, currentQrCodes = {
+ "latest": placeholders.loading
+}, serverSockets = {};
+var io, express, app, gClient, PORT, server;
+var setUpApp = function () { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, Promise.resolve().then(function () { return require('express'); })];
+ case 1:
+ express = (_a.sent())["default"];
+ app = express();
+ app.use(express.static(__dirname + '/node_modules'));
+ app.get('/', function (req, res) {
+ res.sendFile(__dirname + '/index.html');
+ });
+ app.get('/qr', function (req, res) {
+ var sessionId = req.query.sessionId;
+ var qr = sessionId ? currentQrCodes[sessionId] || currentQrCodes.latest : currentQrCodes.latest;
+ res.writeHead(200, { 'Content-Type': 'image/png' });
+ res.write(Buffer.from(qr.replace('data:image/png;base64,', ''), 'base64'), 'utf8');
+ res.end();
+ });
+ return [2 /*return*/];
+ }
+ });
+}); };
+function popup(config) {
+ return __awaiter(this, void 0, void 0, function () {
+ var _p, preferredPort, popupListener, os, appName, hasChrome;
+ var _this = this;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, setUpApp()];
+ case 1:
+ _a.sent();
+ _p = process.env.PORT || config.port;
+ preferredPort = typeof config.popup === "boolean" && config.popup && _p ? Number(_p) : config.popup;
+ popupListener = events_1.ev.on('**', function (data, sessionId, namespace) { return __awaiter(_this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (namespace === null || namespace === void 0 ? void 0 : namespace.includes("sessionData"))
+ return [2 /*return*/];
+ if (!gClient) return [3 /*break*/, 3];
+ return [4 /*yield*/, gClient.send({ data: data, sessionId: sessionId, namespace: namespace })];
+ case 1:
+ _a.sent();
+ if (!((data === null || data === void 0 ? void 0 : data.includes) && (data === null || data === void 0 ? void 0 : data.includes("ready for account")))) return [3 /*break*/, 3];
+ return [4 /*yield*/, gClient.send({ data: config === null || config === void 0 ? void 0 : config.apiHost, sessionId: sessionId, namespace: "ready" })];
+ case 2:
+ _a.sent();
+ _a.label = 3;
+ case 3:
+ if (namespace === 'qr') {
+ currentQrCodes['latest'] = currentQrCodes[sessionId] = data;
+ }
+ if ((data === null || data === void 0 ? void 0 : data.includes) && (data === null || data === void 0 ? void 0 : data.includes("Authenticated"))) {
+ currentQrCodes['latest'] = currentQrCodes[sessionId] = placeholders.authenticated;
+ }
+ if (!((data === null || data === void 0 ? void 0 : data.includes) && (data === null || data === void 0 ? void 0 : data.includes("ready for account")))) return [3 /*break*/, 5];
+ //@ts-ignore
+ popupListener.off();
+ return [4 /*yield*/, (0, exports.closeHttp)()];
+ case 4:
+ _a.sent();
+ _a.label = 5;
+ case 5: return [2 /*return*/];
+ }
+ });
+ }); }, { objectify: true });
+ /**
+ * There should only be one instance of this open. If the server is already running, respond with the address.
+ */
+ if (server)
+ return [2 /*return*/, "http://localhost:".concat(PORT)];
+ return [4 /*yield*/, (0, get_port_1["default"])({ host: 'localhost', port: typeof preferredPort == 'number' ? [preferredPort, 7000, 7001, 7002] : [7000, 7001, 7002] })];
+ case 2:
+ PORT = _a.sent();
+ logging_1.log.info("popup port set to ".concat(PORT));
+ server = http_1["default"].createServer(app);
+ if (!(config === null || config === void 0 ? void 0 : config.qrPopUpOnly)) {
+ io = new socket_io_1.Server(server);
+ io.on('connection', function (client) {
+ gClient = client;
+ gClient.send({ data: 'CONNECTED', sessionId: (config === null || config === void 0 ? void 0 : config.sessionId) || 'session', namespace: 'SOCKET' });
+ gClient.send({ data: (config === null || config === void 0 ? void 0 : config.sessionId) ? currentQrCodes[config === null || config === void 0 ? void 0 : config.sessionId] || currentQrCodes.latest : currentQrCodes.latest, sessionId: (config === null || config === void 0 ? void 0 : config.sessionId) || 'session', namespace: 'qr' });
+ });
+ }
+ server.on("connection", function (conn) {
+ var key = conn.remoteAddress + ':' + conn.remotePort;
+ serverSockets[key] = conn;
+ conn.on("close", function () {
+ delete serverSockets[key];
+ });
+ });
+ server.listen(PORT);
+ (0, tools_1.processSendData)({ port: PORT });
+ os = (0, os_name_1["default"])();
+ appName = os.includes('macOS') ? 'google chrome' : os.includes('Windows') ? 'chrome' : 'google-chrome';
+ return [4 /*yield*/, (0, command_exists_1["default"])(appName).then(function () { return true; })["catch"](function () { return false; })];
+ case 3:
+ hasChrome = _a.sent();
+ if (!hasChrome) return [3 /*break*/, 7];
+ if (!!(config === null || config === void 0 ? void 0 : config.inDocker)) return [3 /*break*/, 5];
+ return [4 /*yield*/, (0, open_1["default"])("http://localhost:".concat(PORT).concat((config === null || config === void 0 ? void 0 : config.qrPopUpOnly) ? "/qr" : ""), { app: {
+ name: (config === null || config === void 0 ? void 0 : config.executablePath) || appName,
+ arguments: ['--incognito']
+ }, allowNonzeroExitCode: true })["catch"](function () { return; })];
+ case 4:
+ _a.sent();
+ return [3 /*break*/, 6];
+ case 5: return [2 /*return*/, "http://localhost:".concat(PORT)];
+ case 6: return [3 /*break*/, 8];
+ case 7: return [2 /*return*/, "http://localhost:".concat(PORT).concat((config === null || config === void 0 ? void 0 : config.qrPopUpOnly) ? '/qr' : '')];
+ case 8: return [2 /*return*/, "http://localhost:".concat(PORT).concat((config === null || config === void 0 ? void 0 : config.qrPopUpOnly) ? '/qr' : '')];
+ }
+ });
+ });
+}
+exports.popup = popup;
+var closeHttp = function () { return __awaiter(void 0, void 0, void 0, function () {
+ var key;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!server)
+ return [2 /*return*/];
+ for (key in serverSockets) {
+ serverSockets[key].destroy();
+ }
+ return [4 /*yield*/, new Promise(function (resolve) { return server.close(resolve); })];
+ case 1: return [2 /*return*/, _a.sent()];
+ }
+ });
+}); };
+exports.closeHttp = closeHttp;
diff --git a/src/controllers/script_preloader.js b/src/controllers/script_preloader.js
new file mode 100644
index 0000000000..5935286278
--- /dev/null
+++ b/src/controllers/script_preloader.js
@@ -0,0 +1,110 @@
+"use strict";
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.scriptLoader = exports.ScriptLoader = void 0;
+var path = require("path");
+var fs = require("fs");
+var logging_1 = require("../logging/logging");
+var read = function (_path) { return new Promise(function (resolve, reject) {
+ fs.readFile(require.resolve(path.join(__dirname, '../lib', _path)), 'utf8', function (err, file) {
+ if (err)
+ reject(err);
+ resolve(file);
+ });
+}); };
+var ScriptLoader = /** @class */ (function () {
+ function ScriptLoader() {
+ this.scripts = [
+ // stage 1
+ 'jsSha.min.js',
+ 'qr.min.js',
+ 'base64.js',
+ 'hash.js',
+ //stage 2
+ 'wapi.js',
+ //stage 3,
+ 'launch.js'
+ ];
+ this.contentRegistry = {};
+ this.contentRegistry = {};
+ }
+ ScriptLoader.prototype.loadScripts = function () {
+ return __awaiter(this, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, Promise.all(this.scripts.map(this.getScript.bind(this)))];
+ case 1:
+ _a.sent();
+ return [2 /*return*/, this.contentRegistry];
+ }
+ });
+ });
+ };
+ ScriptLoader.prototype.getScript = function (scriptName) {
+ return __awaiter(this, void 0, void 0, function () {
+ var _a, _b;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ if (!!this.contentRegistry[scriptName]) return [3 /*break*/, 2];
+ _a = this.contentRegistry;
+ _b = scriptName;
+ return [4 /*yield*/, read(scriptName)];
+ case 1:
+ _a[_b] = _c.sent();
+ logging_1.log.info("SCRIPT READY: ".concat(scriptName, " ").concat(this.contentRegistry[scriptName].length));
+ return [3 /*break*/, 3];
+ case 2:
+ logging_1.log.info("GET SCRIPT: ".concat(scriptName, " ").concat(this.contentRegistry[scriptName].length));
+ _c.label = 3;
+ case 3: return [2 /*return*/, this.contentRegistry[scriptName]];
+ }
+ });
+ });
+ };
+ ScriptLoader.prototype.flush = function () {
+ this.contentRegistry = {};
+ };
+ ScriptLoader.prototype.getScripts = function () {
+ return this.contentRegistry;
+ };
+ return ScriptLoader;
+}());
+exports.ScriptLoader = ScriptLoader;
+var scriptLoader = new ScriptLoader();
+exports.scriptLoader = scriptLoader;
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000000..4831293ca6
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,36 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __exportStar = (this && this.__exportStar) || function(m, exports) {
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
+};
+exports.__esModule = true;
+exports.SimpleListener = exports.Client = exports.Spin = exports.ev = exports.create = void 0;
+var Client_1 = require("./api/Client");
+exports.Client = Client_1.Client;
+var model_1 = require("./api/model");
+exports.SimpleListener = model_1.SimpleListener;
+__exportStar(require("./api/model"), exports);
+__exportStar(require("./api/Client"), exports);
+var initializer_1 = require("./controllers/initializer");
+__createBinding(exports, initializer_1, "create");
+__exportStar(require("@open-wa/wa-decrypt"), exports);
+var events_1 = require("./controllers/events");
+__createBinding(exports, events_1, "ev");
+__createBinding(exports, events_1, "Spin");
+__exportStar(require("./utils/tools"), exports);
+__exportStar(require("./logging/logging"), exports);
+__exportStar(require("./structures/preProcessors"), exports);
+__exportStar(require("@open-wa/wa-automate-socket-client"), exports);
+//dont need to export this
+// export { getConfigWithCase } from './utils/configSchema'
+__exportStar(require("./build/build-postman"), exports);
diff --git a/src/logging/custom_transport.js b/src/logging/custom_transport.js
new file mode 100644
index 0000000000..c23a512e3c
--- /dev/null
+++ b/src/logging/custom_transport.js
@@ -0,0 +1,67 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+exports.__esModule = true;
+exports.NoOpTransport = exports.LogToEvTransport = void 0;
+var winston_transport_1 = require("winston-transport");
+var events_1 = require("../controllers/events");
+var LogToEvTransport = /** @class */ (function (_super) {
+ __extends(LogToEvTransport, _super);
+ function LogToEvTransport(opts) {
+ return _super.call(this, opts) || this;
+ }
+ LogToEvTransport.prototype.log = function (info, callback) {
+ var _this = this;
+ setImmediate(function () {
+ _this.emit('logged', info);
+ });
+ events_1.ev.emit("DEBUG.".concat(info.level), Object.keys(info).reduce(function (p, c) {
+ var _a;
+ return (p = __assign(__assign({}, p), (_a = {}, _a[c] = info[c], _a)));
+ }, {}));
+ if (callback)
+ return callback(null, true);
+ };
+ return LogToEvTransport;
+}(winston_transport_1["default"]));
+exports.LogToEvTransport = LogToEvTransport;
+var NoOpTransport = /** @class */ (function (_super) {
+ __extends(NoOpTransport, _super);
+ function NoOpTransport(opts) {
+ return _super.call(this, opts) || this;
+ }
+ NoOpTransport.prototype.log = function (info, callback) {
+ var _this = this;
+ setImmediate(function () {
+ _this.emit('logged', info);
+ });
+ if (callback)
+ return callback(null, true);
+ };
+ return NoOpTransport;
+}(winston_transport_1["default"]));
+exports.NoOpTransport = NoOpTransport;
diff --git a/src/logging/logging.js b/src/logging/logging.js
new file mode 100644
index 0000000000..061d2959c5
--- /dev/null
+++ b/src/logging/logging.js
@@ -0,0 +1,188 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+exports.__esModule = true;
+exports.setupLogging = exports.addSysLogTransport = exports.addRotateFileLogTransport = exports.log = void 0;
+var os_1 = require("os");
+var winston = require("winston");
+var winston_daily_rotate_file_1 = require("winston-daily-rotate-file");
+var winston_syslog_1 = require("winston-syslog");
+var custom_transport_1 = require("./custom_transport");
+var _a = winston.format, combine = _a.combine, timestamp = _a.timestamp;
+var traverse_1 = require("traverse");
+var full_1 = require("klona/full");
+var truncateLength = 200;
+var _evSet = false, _consoleSet = false, d = Date.now();
+var sensitiveKeys = [
+ /cookie/i,
+ /sessionData/i,
+ /passw(or)?d/i,
+ /^pw$/,
+ /^pass$/i,
+ /secret/i,
+ /token/i,
+ /api[-._]?key/i,
+];
+var getCircularReplacer = function () {
+ var seen = new WeakSet();
+ return function (_key, value) {
+ if (typeof value === "object" && value !== null) {
+ if (seen.has(value)) {
+ return "[Circular]";
+ }
+ seen.add(value);
+ }
+ return value;
+ };
+};
+var k = function (obj) {
+ try {
+ return (0, full_1.klona)(obj);
+ }
+ catch (error) {
+ return (0, full_1.klona)(JSON.parse(JSON.stringify(obj, getCircularReplacer())));
+ }
+};
+function isSensitiveKey(keyStr) {
+ if (keyStr && typeof keyStr == "string") {
+ return sensitiveKeys.some(function (regex) { return regex.test(keyStr); });
+ }
+}
+function redactObject(obj) {
+ (0, traverse_1["default"])(obj).forEach(function redactor() {
+ if (isSensitiveKey(this.key)) {
+ this.update("[REDACTED]");
+ }
+ else if (typeof this.node === 'string' && this.node.length > truncateLength) {
+ this.update(truncate(this.node, truncateLength));
+ }
+ });
+}
+function redact(obj) {
+ var copy = k(obj); // Making a deep copy to prevent side effects
+ redactObject(copy);
+ var splat = copy[Symbol["for"]("splat")];
+ redactObject(splat); // Specifically redact splat Symbol
+ return copy;
+}
+function truncate(str, n) {
+ return str.length > n ? str.substr(0, n - 1) + '...[TRUNCATED]...' : str;
+}
+var formatRedact = winston.format(redact);
+var stringSaver = winston.format(function (info) {
+ var copy = k(info);
+ var splat = copy[Symbol["for"]("splat")];
+ if (splat) {
+ copy.message = "".concat(copy.message, " ").concat(splat.filter(function (x) { return typeof x !== 'object'; }).join(' '));
+ copy[Symbol["for"]("splat")] = splat.filter(function (x) { return typeof x == 'object'; });
+ return copy;
+ }
+ return info;
+});
+/**
+ * To prevent "Attempt to write logs with no transports" error
+ */
+var placeholderTransport = new custom_transport_1.NoOpTransport();
+var makeLogger = function () {
+ return winston.createLogger({
+ format: combine(stringSaver(), timestamp(), winston.format.json(), formatRedact(), winston.format.splat(), winston.format.simple()),
+ levels: winston.config.syslog.levels,
+ transports: [placeholderTransport]
+ });
+};
+/**
+ * You can access the log in your code and add your own custom transports
+ * https://github.com/winstonjs/winston#transports
+ * see [Logger](https://github.com/winstonjs/winston#transports) for more details.
+ *
+ * Here is an example of adding the GCP stackdriver transport:
+ *
+ * ```
+ * import { log } from '@open-wa/wa-automate'
+ * import { LoggingWinston } from '@google-cloud/logging-winston';
+ *
+ * const gcpTransport = new LoggingWinston({
+ * projectId: 'your-project-id',
+ * keyFilename: '/path/to/keyfile.json'
+ * });
+ *
+ * ...
+ * log.add(
+ * gcpTransport
+ * )
+ *
+ * //Congrats! Now all of your session logs will also go to GCP Stackdriver
+ * ```
+ */
+exports.log = makeLogger();
+if (exports.log.warning && !exports.log.warn)
+ exports.log.warn = exports.log.warning;
+if (exports.log.alert && !exports.log.help)
+ exports.log.help = exports.log.alert;
+var addRotateFileLogTransport = function (options) {
+ if (options === void 0) { options = {}; }
+ exports.log.add(new winston_daily_rotate_file_1["default"](__assign({ filename: 'application-%DATE%.log', datePattern: 'YYYY-MM-DD-HH', zippedArchive: true, maxSize: '2m', maxFiles: '14d' }, options)));
+};
+exports.addRotateFileLogTransport = addRotateFileLogTransport;
+/**
+ * @private
+ */
+var addSysLogTransport = function (options) {
+ if (options === void 0) { options = {}; }
+ exports.log.add(new winston_syslog_1.Syslog(__assign({ localhost: os_1["default"].hostname() }, options)));
+};
+exports.addSysLogTransport = addSysLogTransport;
+var enableConsoleLogger = function (options) {
+ if (options === void 0) { options = {}; }
+ if (_consoleSet)
+ return;
+ exports.log.add(new winston.transports.Console(__assign({ level: 'debug', timestamp: timestamp() }, options)));
+ _consoleSet = true;
+};
+function enableLogToEv(options) {
+ if (options === void 0) { options = {}; }
+ if (_evSet)
+ return;
+ exports.log.add(new custom_transport_1.LogToEvTransport(__assign({ format: winston.format.json() }, options)));
+ _evSet = true;
+}
+/**
+ * @private
+ */
+var setupLogging = function (logging, sessionId) {
+ if (sessionId === void 0) { sessionId = "session"; }
+ var currentlySetup = [];
+ var _logging = logging.map(function (l) {
+ if (l.done)
+ return l;
+ if (l.type === 'console') {
+ enableConsoleLogger(__assign({}, (l.options || {})));
+ }
+ else if (l.type === 'ev') {
+ enableLogToEv(__assign({}, (l.options || {})));
+ }
+ else if (l.type === 'file') {
+ (0, exports.addRotateFileLogTransport)(__assign({}, (l.options || {})));
+ }
+ else if (l.type === 'syslog') {
+ (0, exports.addSysLogTransport)(__assign(__assign({}, (l.options || {})), { appName: "owa-".concat(sessionId, "-").concat(d) }));
+ }
+ currentlySetup.push(l);
+ return __assign(__assign({}, l), { done: true });
+ });
+ currentlySetup.map(function (l) {
+ exports.log.info("Set up logging for ".concat(l.type), l.options);
+ return l;
+ });
+ return _logging;
+};
+exports.setupLogging = setupLogging;
diff --git a/src/structures/Collector.js b/src/structures/Collector.js
new file mode 100644
index 0000000000..0c09db3c3a
--- /dev/null
+++ b/src/structures/Collector.js
@@ -0,0 +1,545 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
+var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
+ return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
+ function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
+ function fulfill(value) { resume("next", value); }
+ function reject(value) { resume("throw", value); }
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
+};
+var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+ if (ar || !(i in from)) {
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+ ar[i] = from[i];
+ }
+ }
+ return to.concat(ar || Array.prototype.slice.call(from));
+};
+exports.__esModule = true;
+exports.Collector = exports.Collection = void 0;
+/* eslint-disable @typescript-eslint/no-unused-vars */
+/**
+ * This code is a copy of the Discord Collector: https://github.com/discordjs/discord.js/blob/stable/src/structures/interfaces/Collector.js
+ *
+ * Please see: https://discord.js.org/#/docs/main/stable/class/Collector
+ */
+// import { EventEmitter2 } from 'eventemitter2';
+var collection_1 = require("@discordjs/collection");
+var events_1 = require("events");
+var Collection = /** @class */ (function (_super) {
+ __extends(Collection, _super);
+ function Collection() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ Collection.prototype.toJSON = function () {
+ return this.map(function (e) { return (typeof e.toJSON === 'function' ? e.toJSON() : e); });
+ };
+ return Collection;
+}(collection_1.Collection));
+exports.Collection = Collection;
+/**
+ * Abstract class for defining a new Collector.
+ * @abstract
+ */
+var Collector = /** @class */ (function (_super) {
+ __extends(Collector, _super);
+ function Collector(filter, options) {
+ if (options === void 0) { options = {}; }
+ var _this = _super.call(this) || this;
+ /**
+ * Timeouts set by {@link BaseClient#setTimeout} that are still active
+ * @type {Set}
+ * @private
+ */
+ _this._timeouts = new Set();
+ /**
+ * Intervals set by {@link BaseClient#setInterval} that are still active
+ * @type {Set}
+ * @private
+ */
+ _this._intervals = new Set();
+ /**
+ * Intervals set by {@link BaseClient#setImmediate} that are still active
+ * @type {Set}
+ * @private
+ */
+ _this._immediates = new Set();
+ /**
+ * The filter applied to this collector
+ * @type {CollectorFilter}
+ */
+ _this.filter = filter;
+ /**
+ * The options of this collector
+ * @type {CollectorOptions}
+ */
+ _this.options = options;
+ /**
+ * The items collected by this collector
+ * @type {Collection}
+ */
+ _this.collected = new Collection();
+ /**
+ * Whether this collector has finished collecting
+ * @type {boolean}
+ */
+ _this.ended = false;
+ /**
+ * Timeout for cleanup
+ * @type {?Timeout}
+ * @private
+ */
+ _this._timeout = null;
+ /**
+ * Timeout for cleanup due to inactivity
+ * @type {?Timeout}
+ * @private
+ */
+ _this._idletimeout = null;
+ _this.handleCollect = _this.handleCollect.bind(_this);
+ _this.handleDispose = _this.handleDispose.bind(_this);
+ if (options.time)
+ _this._timeout = _this.setTimeout(function () { return _this.stop('time'); }, options.time);
+ if (options.idle)
+ _this._idletimeout = _this.setTimeout(function () { return _this.stop('idle'); }, options.idle);
+ return _this;
+ }
+ /**
+ * Call this to handle an event as a collectable element. Accepts any event data as parameters.
+ * @param {...*} args The arguments emitted by the listener
+ * @emits Collector#collect
+ */
+ Collector.prototype.handleCollect = function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ return __awaiter(this, void 0, void 0, function () {
+ var collect, _a;
+ var _this = this;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ collect = this.collect.apply(this, args);
+ _a = collect;
+ if (!_a) return [3 /*break*/, 2];
+ return [4 /*yield*/, this.filter.apply(this, __spreadArray(__spreadArray([], args, false), [this.collected], false))];
+ case 1:
+ _a = (_b.sent());
+ _b.label = 2;
+ case 2:
+ if (_a) {
+ this.collected.set(collect, args[0]);
+ /**
+ * Emitted whenever an element is collected.
+ * @event Collector#collect
+ * @param {...*} args The arguments emitted by the listener
+ */
+ this.emit.apply(this, __spreadArray(['collect'], args, false));
+ if (this._idletimeout) {
+ this.clearTimeout(this._idletimeout);
+ this._idletimeout = this.setTimeout(function () { return _this.stop('idle'); }, this.options.idle);
+ }
+ }
+ this.checkEnd();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /**
+ * Call this to remove an element from the collection. Accepts any event data as parameters.
+ * @param {...*} args The arguments emitted by the listener
+ * @emits Collector#dispose
+ */
+ Collector.prototype.handleDispose = function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ return __awaiter(this, void 0, void 0, function () {
+ var dispose, _a;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (!this.options.dispose)
+ return [2 /*return*/];
+ dispose = this.dispose.apply(this, args);
+ _a = !dispose;
+ if (_a) return [3 /*break*/, 2];
+ return [4 /*yield*/, this.filter.apply(this, args)];
+ case 1:
+ _a = !(_b.sent());
+ _b.label = 2;
+ case 2:
+ if (_a || !this.collected.has(dispose))
+ return [2 /*return*/];
+ this.collected["delete"](dispose);
+ /**
+ * Emitted whenever an element is disposed of.
+ * @event Collector#dispose
+ * @param {...*} args The arguments emitted by the listener
+ */
+ this.emit.apply(this, __spreadArray(['dispose'], args, false));
+ this.checkEnd();
+ return [2 /*return*/];
+ }
+ });
+ });
+ };
+ Object.defineProperty(Collector.prototype, "next", {
+ /**
+ * Returns a promise that resolves with the next collected element;
+ * rejects with collected elements if the collector finishes without receiving a next element
+ * @type {Promise}
+ * @readonly
+ */
+ get: function () {
+ var _this = this;
+ return new Promise(function (resolve, reject) {
+ if (_this.ended) {
+ reject(_this.collected);
+ return;
+ }
+ var cleanup = function () {
+ _this.removeListener('collect', onCollect);
+ _this.removeListener('end', onEnd);
+ };
+ var onCollect = function (item) {
+ cleanup();
+ resolve(item);
+ };
+ var onEnd = function () {
+ cleanup();
+ reject(_this.collected); // eslint-disable-line prefer-promise-reject-errors
+ };
+ _this.on('collect', onCollect);
+ _this.on('end', onEnd);
+ });
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Stops this collector and emits the `end` event.
+ * @param {string} [reason='user'] The reason this collector is ending
+ * @emits Collector#end
+ */
+ Collector.prototype.stop = function (reason) {
+ if (reason === void 0) { reason = 'user'; }
+ if (this.ended)
+ return;
+ if (this._timeout) {
+ this.clearTimeout(this._timeout);
+ this._timeout = null;
+ }
+ if (this._idletimeout) {
+ this.clearTimeout(this._idletimeout);
+ this._idletimeout = null;
+ }
+ this.ended = true;
+ /**
+ * Emitted when the collector is finished collecting.
+ * @event Collector#end
+ * @param {Collection} collected The elements collected by the collector
+ * @param {string} reason The reason the collector ended
+ */
+ this.emit('end', this.collected, reason);
+ };
+ /**
+ * Resets the collectors timeout and idle timer.
+ * @param {Object} [options] Options
+ * @param {number} [options.time] How long to run the collector for in milliseconds
+ * @param {number} [options.idle] How long to stop the collector after inactivity in milliseconds
+ */
+ Collector.prototype.resetTimer = function (_a) {
+ var _this = this;
+ var _b = _a === void 0 ? {
+ time: null,
+ idle: null
+ } : _a, time = _b.time, idle = _b.idle;
+ if (this._timeout) {
+ this.clearTimeout(this._timeout);
+ this._timeout = this.setTimeout(function () { return _this.stop('time'); }, time || this.options.time);
+ }
+ if (this._idletimeout) {
+ this.clearTimeout(this._idletimeout);
+ this._idletimeout = this.setTimeout(function () { return _this.stop('idle'); }, idle || this.options.idle);
+ }
+ };
+ /**
+ * Checks whether the collector should end, and if so, ends it.
+ */
+ Collector.prototype.checkEnd = function () {
+ var reason = this.endReason();
+ if (reason)
+ this.stop(reason);
+ };
+ /**
+ * Allows collectors to be consumed with for-await-of loops
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of}
+ */
+ Collector.prototype[Symbol.asyncIterator] = function () {
+ return __asyncGenerator(this, arguments, function _a() {
+ var queue, onCollect;
+ var _this = this;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ queue = [];
+ onCollect = function (item) { return queue.push(item); };
+ this.on('collect', onCollect);
+ _b.label = 1;
+ case 1:
+ _b.trys.push([1, , 9, 10]);
+ _b.label = 2;
+ case 2:
+ if (!(queue.length || !this.ended)) return [3 /*break*/, 8];
+ if (!queue.length) return [3 /*break*/, 5];
+ return [4 /*yield*/, __await(queue.shift())];
+ case 3: return [4 /*yield*/, _b.sent()];
+ case 4:
+ _b.sent();
+ return [3 /*break*/, 7];
+ case 5:
+ // eslint-disable-next-line no-await-in-loop
+ return [4 /*yield*/, __await(new Promise(function (resolve) {
+ var tick = function () {
+ _this.removeListener('collect', tick);
+ _this.removeListener('end', tick);
+ return resolve(true);
+ };
+ _this.on('collect', tick);
+ _this.on('end', tick);
+ }))];
+ case 6:
+ // eslint-disable-next-line no-await-in-loop
+ _b.sent();
+ _b.label = 7;
+ case 7: return [3 /*break*/, 2];
+ case 8: return [3 /*break*/, 10];
+ case 9:
+ this.removeListener('collect', onCollect);
+ return [7 /*endfinally*/];
+ case 10: return [2 /*return*/];
+ }
+ });
+ });
+ };
+ /* eslint-disable no-empty-function, valid-jsdoc */
+ /**
+ * Handles incoming events from the `handleCollect` function. Returns null if the event should not
+ * be collected, or returns an object describing the data that should be stored.
+ * @see Collector#handleCollect
+ * @param {...*} _args Any args the event listener emits
+ * @returns the id if the object should be collected, if it shouldnt be collected then it will return null or false.
+ * @abstract
+ */
+ Collector.prototype.collect = function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ throw new Error("abstractMethod not implemented");
+ };
+ /**
+ * Handles incoming events from the `handleDispose`. Returns null if the event should not
+ * be disposed, or returns the key that should be removed.
+ * @see Collector#handleDispose
+ * @param {...*} args Any args the event listener emits
+ * @returns {?*} Key to remove from the collection, if any
+ * @abstract
+ */
+ Collector.prototype.dispose = function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ throw new Error("abstractMethod not implemented");
+ };
+ /**
+ * The reason this collector has ended or will end with.
+ * @returns {?string} Reason to end the collector, if any
+ * @abstract
+ */
+ Collector.prototype.endReason = function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ throw new Error("abstractMethod not implemented");
+ };
+ /**
+ * Clears a timeout.
+ * @param {Timeout} timeout Timeout to cancel
+ */
+ Collector.prototype.clearTimeout = function (timeout) {
+ clearTimeout(timeout);
+ this._timeouts["delete"](timeout);
+ };
+ /**
+ * Sets an interval that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait between executions (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ Collector.prototype.setInterval = function (fn, delay) {
+ var args = [];
+ for (var _i = 2; _i < arguments.length; _i++) {
+ args[_i - 2] = arguments[_i];
+ }
+ var interval = setInterval.apply(void 0, __spreadArray([fn, delay], args, false));
+ this._intervals.add(interval);
+ return interval;
+ };
+ /**
+ * Clears an interval.
+ * @param {Timeout} interval Interval to cancel
+ */
+ Collector.prototype.clearInterval = function (interval) {
+ clearInterval(interval);
+ this._intervals["delete"](interval);
+ };
+ /**
+ * Sets an immediate that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {...*} args Arguments for the function
+ * @returns {Immediate}
+ */
+ Collector.prototype.setImmediate = function (fn) {
+ var args = [];
+ for (var _i = 1; _i < arguments.length; _i++) {
+ args[_i - 1] = arguments[_i];
+ }
+ var immediate = setImmediate.apply(void 0, __spreadArray([fn], args, false));
+ this._immediates.add(immediate);
+ return immediate;
+ };
+ /**
+ * Clears an immediate.
+ * @param {Immediate} immediate Immediate to cancel
+ */
+ Collector.prototype.clearImmediate = function (immediate) {
+ clearImmediate(immediate);
+ this._immediates["delete"](immediate);
+ };
+ /**
+ * Increments max listeners by one, if they are not zero.
+ * @private
+ */
+ Collector.prototype.incrementMaxListeners = function () {
+ var maxListeners = this.getMaxListeners();
+ if (maxListeners !== 0) {
+ this.setMaxListeners(maxListeners + 1);
+ }
+ };
+ /**
+ * Decrements max listeners by one, if they are not zero.
+ * @private
+ */
+ Collector.prototype.decrementMaxListeners = function () {
+ var maxListeners = this.getMaxListeners();
+ if (maxListeners !== 0) {
+ this.setMaxListeners(maxListeners - 1);
+ }
+ };
+ /**
+ * Sets a timeout that will be automatically cancelled if the client is destroyed.
+ * @param {Function} fn Function to execute
+ * @param {number} delay Time to wait before executing (in milliseconds)
+ * @param {...*} args Arguments for the function
+ * @returns {Timeout}
+ */
+ Collector.prototype.setTimeout = function (fn, delay) {
+ var _this = this;
+ var args = [];
+ for (var _i = 2; _i < arguments.length; _i++) {
+ args[_i - 2] = arguments[_i];
+ }
+ var timeout = setTimeout(function () {
+ fn.apply(void 0, args);
+ _this._timeouts["delete"](timeout);
+ }, delay);
+ this._timeouts.add(timeout);
+ return timeout;
+ };
+ /**
+ * Destroys all assets used by the base client.
+ */
+ Collector.prototype.destroy = function () {
+ for (var _i = 0, _a = this._timeouts; _i < _a.length; _i++) {
+ var t = _a[_i];
+ this.clearTimeout(t);
+ }
+ for (var _b = 0, _c = this._intervals; _b < _c.length; _b++) {
+ var i = _c[_b];
+ this.clearInterval(i);
+ }
+ for (var _d = 0, _e = this._immediates; _d < _e.length; _d++) {
+ var i = _e[_d];
+ this.clearImmediate(i);
+ }
+ this._timeouts.clear();
+ this._intervals.clear();
+ this._immediates.clear();
+ };
+ return Collector;
+}(events_1.EventEmitter));
+exports.Collector = Collector;
diff --git a/src/structures/MessageCollector.js b/src/structures/MessageCollector.js
new file mode 100644
index 0000000000..1a0c28f176
--- /dev/null
+++ b/src/structures/MessageCollector.js
@@ -0,0 +1,167 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+exports.__esModule = true;
+exports.MessageCollector = void 0;
+var events_1 = require("../api/model/events");
+var Collector_1 = require("./Collector");
+/**
+ * @typedef {CollectorOptions} MessageCollectorOptions
+ * @property {number} max The maximum amount of messages to collect
+ * @property {number} maxProcessed The maximum amount of messages to process
+ */
+/**
+ * Collects messages on a chat.
+ * Will automatically stop if the chat (`'chatDelete'`) is deleted.
+ * @extends {Collector}
+ */
+var MessageCollector = /** @class */ (function (_super) {
+ __extends(MessageCollector, _super);
+ /**
+ * @param {string} sessionId The id of the session
+ * @param {string} instanceId The id of the current instance of the session (see: client.getInstanceId)
+ * @param {ChatId} chatId The chat
+ * @param {CollectorFilter} filter The filter to be applied to this collector
+ * @param {MessageCollectorOptions} options The options to be applied to this collector
+ * @param {EventEmitter2} openWaEventEmitter The EventEmitter2 that fires all open-wa events. In local instances of the library, this is the global `ev` object.
+ * @emits MessageCollector#Message
+ */
+ function MessageCollector(sessionId, instanceId, chat, filter, options, openWaEventEmitter) {
+ if (options === void 0) { options = {}; }
+ var _this = _super.call(this, filter, options) || this;
+ _this.ev = openWaEventEmitter;
+ _this.sessionId = sessionId;
+ _this.instanceId = instanceId;
+ /**
+ * The chat
+ * @type {TextBasedChannel}
+ */
+ _this.chat = chat;
+ /**
+ * Total number of messages that were received in the chat during message collection
+ * @type {number}
+ */
+ _this.received = 0;
+ _this._handleChatDeletion = _this._handleChatDeletion.bind(_this);
+ _this._handleGuildDeletion = _this._handleGuildDeletion.bind(_this);
+ var collectHandler = _this.wrapHandler(_this.handleCollect);
+ var disposeHandler = _this.wrapHandler(_this.handleDispose);
+ var deleteHandler = _this.wrapHandler(_this._handleChatDeletion);
+ var groupRemovalHandler = _this.wrapHandler(_this._handleGroupRemoval);
+ _this.incrementMaxListeners();
+ _this.ev.on(_this.eventSignature(events_1.SimpleListener.Message), collectHandler);
+ _this.ev.on(_this.eventSignature(events_1.SimpleListener.MessageDeleted), disposeHandler);
+ _this.ev.on(_this.eventSignature(events_1.SimpleListener.ChatDeleted), deleteHandler);
+ _this.ev.on(_this.eventSignature(events_1.SimpleListener.RemovedFromGroup), groupRemovalHandler);
+ // this.ev.on(Events.GUILD_DELETE, this._handleGuildDeletion);
+ _this.once('end', function () {
+ _this.ev.removeListener(_this.eventSignature(events_1.SimpleListener.Message), collectHandler);
+ _this.ev.removeListener(_this.eventSignature(events_1.SimpleListener.MessageDeleted), disposeHandler);
+ _this.ev.removeListener(_this.eventSignature(events_1.SimpleListener.ChatDeleted), deleteHandler);
+ _this.ev.removeListener(_this.eventSignature(events_1.SimpleListener.RemovedFromGroup), groupRemovalHandler);
+ // this.ev.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion);
+ _this.decrementMaxListeners();
+ });
+ return _this;
+ }
+ /**
+ * Handles a message for possible collection.
+ * @param {Message} message The message that could be collected
+ * @returns {?Snowflake}
+ * @private
+ */
+ MessageCollector.prototype.collect = function (message) {
+ /**
+ * Emitted whenever a message is collected.
+ * @event MessageCollector#collect
+ * @param {Message} message The message that was collected
+ */
+ if (message.chat.id !== this.chat)
+ return null;
+ this.received++;
+ return message.id;
+ };
+ /**
+ * Handles a message for possible disposal.
+ * @param {Message} message The message that could be disposed of
+ * @returns {?Snowflake}
+ */
+ MessageCollector.prototype.dispose = function (message) {
+ /**
+ * Emitted whenever a message is disposed of.
+ * @event MessageCollector#dispose
+ * @param {Message} message The message that was disposed of
+ */
+ return message.chat.id === this.chat ? message.id : null;
+ };
+ /**
+ * Checks after un/collection to see if the collector is done.
+ * @returns {?string}
+ * @private
+ */
+ MessageCollector.prototype.endReason = function () {
+ if (this.options.max && this.collected.size >= this.options.max)
+ return 'limit';
+ if (this.options.maxProcessed && this.received === this.options.maxProcessed)
+ return 'processedLimit';
+ return null;
+ };
+ /**
+ * Handles checking if the chat has been deleted, and if so, stops the collector with the reason 'chatDelete'.
+ * @private
+ * @param {Chat} chat The chat that was deleted
+ * @returns {void}
+ */
+ MessageCollector.prototype._handleChatDeletion = function (chat) {
+ if (chat.id === this.chat) {
+ this.stop('chatDelete');
+ }
+ };
+ /**
+ * Handles checking if the chat has been deleted, and if so, stops the collector with the reason 'chatDelete'.
+ * @private
+ * @param {Chat} chat The group chat that the host account was removed from
+ * @returns {void}
+ */
+ MessageCollector.prototype._handleGroupRemoval = function (chat) {
+ if (chat.id === this.chat) {
+ this.stop('groupRemoval');
+ }
+ };
+ /**
+ *
+ * NOT RELATED TO WA
+ *
+ * Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'.
+ * @private
+ * @param {Guild} guild The guild that was deleted
+ * @returns {void}
+ */
+ MessageCollector.prototype._handleGuildDeletion = function (guild) {
+ console.error('This does not relate to WA', guild);
+ };
+ MessageCollector.prototype.eventSignature = function (event) {
+ return "".concat(event, ".").concat(this.sessionId, ".").concat(this.instanceId);
+ };
+ MessageCollector.prototype.wrapHandler = function (handler) {
+ return function (_a) {
+ var data = _a.data;
+ return handler(data);
+ };
+ };
+ return MessageCollector;
+}(Collector_1.Collector));
+exports.MessageCollector = MessageCollector;
diff --git a/src/structures/preProcessors.js b/src/structures/preProcessors.js
new file mode 100644
index 0000000000..94f8943f4b
--- /dev/null
+++ b/src/structures/preProcessors.js
@@ -0,0 +1,249 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.PREPROCESSORS = exports.MessagePreprocessors = void 0;
+var mime_1 = require("mime");
+var fs_extra_1 = require("fs-extra");
+var config_1 = require("../api/model/config");
+var p_queue_1 = require("p-queue");
+var pico_s3_1 = require("pico-s3");
+var processedFiles = {};
+var uploadQueue;
+var SCRUB = function (message) { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ if (message.deprecatedMms3Url && message.mimetype)
+ return [2 /*return*/, __assign(__assign({}, message), { content: "", body: "" })];
+ return [2 /*return*/, message];
+ });
+}); };
+var BODY_ONLY = function (message) { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ if (message.deprecatedMms3Url && message.mimetype)
+ return [2 /*return*/, __assign(__assign({}, message), { content: "" })];
+ return [2 /*return*/, message];
+ });
+}); };
+var AUTO_DECRYPT = function (message, client) { return __awaiter(void 0, void 0, void 0, function () {
+ var _a;
+ var _b;
+ return __generator(this, function (_c) {
+ switch (_c.label) {
+ case 0:
+ if (!(message.deprecatedMms3Url && message.mimetype)) return [3 /*break*/, 2];
+ _a = [__assign({}, message)];
+ _b = {};
+ return [4 /*yield*/, client.decryptMedia(message)];
+ case 1: return [2 /*return*/, __assign.apply(void 0, _a.concat([(_b.body = _c.sent(), _b)]))];
+ case 2: return [2 /*return*/, message];
+ }
+ });
+}); };
+var AUTO_DECRYPT_SAVE = function (message, client, alreadyBeingProcessed) { return __awaiter(void 0, void 0, void 0, function () {
+ var filename, filePath, mediaData, error_1;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!(message.deprecatedMms3Url && message.mimetype)) return [3 /*break*/, 5];
+ filename = "".concat(message.mId, ".").concat(mime_1["default"].getExtension(message.mimetype));
+ filePath = "media/".concat(filename);
+ if (alreadyBeingProcessed) {
+ return [2 /*return*/, __assign(__assign({}, message), { body: filename, content: "", filePath: filePath })];
+ }
+ _a.label = 1;
+ case 1:
+ _a.trys.push([1, 3, , 4]);
+ return [4 /*yield*/, client.decryptMedia(message)];
+ case 2:
+ mediaData = _a.sent();
+ (0, fs_extra_1.outputFileSync)(filePath, Buffer.from(mediaData.split(",")[1], "base64"));
+ return [3 /*break*/, 4];
+ case 3:
+ error_1 = _a.sent();
+ console.error(error_1);
+ return [2 /*return*/, message];
+ case 4: return [2 /*return*/, __assign(__assign({}, message), { body: filename, content: "", filePath: filePath })];
+ case 5: return [2 /*return*/, message];
+ }
+ });
+}); };
+var UPLOAD_CLOUD = function (message, client, alreadyBeingProcessed) { return __awaiter(void 0, void 0, void 0, function () {
+ var cloudUploadOptions, filename, mediaData, provider, opts_1, dirStrat, directory, url, error_2;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!((message === null || message === void 0 ? void 0 : message.deprecatedMms3Url) && message.mimetype)) return [3 /*break*/, 6];
+ cloudUploadOptions = client.getConfig().cloudUploadOptions;
+ if (message.fromMe && (cloudUploadOptions.ignoreHostAccount || process.env.OW_CLOUD_IGNORE_HOST))
+ return [2 /*return*/, message];
+ if (!uploadQueue) {
+ uploadQueue = new p_queue_1["default"]({ concurrency: 2, interval: 1000, carryoverConcurrencyCount: true, intervalCap: 2 });
+ }
+ filename = "".concat(message.mId || "".concat(Date.now()), ".").concat(mime_1["default"].getExtension(message.mimetype));
+ return [4 /*yield*/, client.decryptMedia(message)];
+ case 1:
+ mediaData = _a.sent();
+ if (!cloudUploadOptions)
+ return [2 /*return*/, message];
+ provider = (process.env.OW_CLOUD_PROVIDER || cloudUploadOptions.provider);
+ opts_1 = {
+ file: mediaData,
+ filename: filename,
+ provider: provider,
+ accessKeyId: process.env.OW_CLOUD_ACCESS_KEY_ID || cloudUploadOptions.accessKeyId,
+ secretAccessKey: process.env.OW_CLOUD_SECRET_ACCESS_KEY || cloudUploadOptions.secretAccessKey,
+ bucket: process.env.OW_CLOUD_BUCKET || cloudUploadOptions.bucket,
+ region: process.env.OW_CLOUD_REGION || cloudUploadOptions.region,
+ public: process.env.OW_CLOUD_PUBLIC && true || cloudUploadOptions.public,
+ headers: cloudUploadOptions.headers
+ };
+ dirStrat = process.env.OW_DIRECTORY || cloudUploadOptions.directory;
+ if (dirStrat) {
+ directory = '';
+ switch (dirStrat) {
+ case config_1.DIRECTORY_STRATEGY.DATE:
+ directory = "".concat(new Date().toISOString().slice(0, 10));
+ break;
+ case config_1.DIRECTORY_STRATEGY.CHAT:
+ directory = "".concat(message.from.replace("@c.us", "").replace("@g.us", ""));
+ break;
+ case config_1.DIRECTORY_STRATEGY.DATE_CHAT:
+ directory = "".concat(new Date().toISOString().slice(0, 10), "/").concat(message.from.replace("@c.us", "").replace("@g.us", ""));
+ break;
+ case config_1.DIRECTORY_STRATEGY.CHAT_DATE:
+ directory = "".concat(message.from.replace("@c.us", "").replace("@g.us", ""), "/").concat(new Date().toISOString().slice(0, 10));
+ break;
+ default:
+ directory = dirStrat;
+ break;
+ }
+ opts_1.directory = directory;
+ }
+ if (!opts_1.accessKeyId) {
+ console.error("UPLOAD ERROR: No accessKeyId provided. If you're using the CLI, set env var OW_CLOUD_ACCESS_KEY_ID");
+ return [2 /*return*/, message];
+ }
+ if (!opts_1.secretAccessKey) {
+ console.error("UPLOAD ERROR: No secretAccessKey provided. If you're using the CLI, set env var OW_CLOUD_SECRET_ACCESS_KEY");
+ return [2 /*return*/, message];
+ }
+ if (!opts_1.bucket) {
+ console.error("UPLOAD ERROR: No bucket provided. If you're using the CLI, set env var OW_CLOUD_BUCKET");
+ return [2 /*return*/, message];
+ }
+ if (!opts_1.provider) {
+ console.error("UPLOAD ERROR: No provider provided. If you're using the CLI, set env var OW_CLOUD_PROVIDER");
+ return [2 /*return*/, message];
+ }
+ url = (0, pico_s3_1.getCloudUrl)(opts_1);
+ if (!(!processedFiles[filename] && !alreadyBeingProcessed)) return [3 /*break*/, 5];
+ processedFiles[filename] = true;
+ _a.label = 2;
+ case 2:
+ _a.trys.push([2, 4, , 5]);
+ return [4 /*yield*/, uploadQueue.add(function () { return (0, pico_s3_1.upload)(opts_1)["catch"](function () { }); })];
+ case 3:
+ _a.sent();
+ return [3 /*break*/, 5];
+ case 4:
+ error_2 = _a.sent();
+ console.error(error_2);
+ return [2 /*return*/, message];
+ case 5: return [2 /*return*/, __assign(__assign({}, message), { cloudUrl: url })];
+ case 6: return [2 /*return*/, message];
+ }
+ });
+}); };
+/**
+ * An object that contains all available [[PREPROCESSORS]].
+ *
+ * [Check out the processor code here](https://github.com/open-wa/wa-automate-nodejs/blob/master/src/structures/preProcessors.ts)
+ */
+exports.MessagePreprocessors = {
+ AUTO_DECRYPT_SAVE: AUTO_DECRYPT_SAVE,
+ AUTO_DECRYPT: AUTO_DECRYPT,
+ BODY_ONLY: BODY_ONLY,
+ SCRUB: SCRUB,
+ UPLOAD_CLOUD: UPLOAD_CLOUD
+};
+/**
+ * A set of easy to use, built-in message processors.
+ *
+ * [Check out the processor code here](https://github.com/open-wa/wa-automate-nodejs/blob/master/src/structures/preProcessors.ts)
+ *
+ */
+var PREPROCESSORS;
+(function (PREPROCESSORS) {
+ /**
+ * This preprocessor scrubs `body` and `content` from media messages.
+ * This would be useful if you want to reduce the message object size because neither of these values represent the actual file, only the thumbnail.
+ */
+ PREPROCESSORS["SCRUB"] = "SCRUB";
+ /**
+ * A preprocessor that limits the amount of base64 data is present in the message object by removing duplication of `body` in `content` by replacing `content` with `""`.
+ */
+ PREPROCESSORS["BODY_ONLY"] = "BODY_ONLY";
+ /**
+ * Replaces the media thumbnail base64 in `body` with the actual file's DataURL.
+ */
+ PREPROCESSORS["AUTO_DECRYPT"] = "AUTO_DECRYPT";
+ /**
+ * Automatically saves the file in a folder named `/media` relative to the process working directory.
+ *
+ * PLEASE NOTE, YOU WILL NEED TO MANUALLY CLEAR THIS FOLDER!!!
+ */
+ PREPROCESSORS["AUTO_DECRYPT_SAVE"] = "AUTO_DECRYPT_SAVE";
+ /**
+ *
+ * Uploads file to a cloud storage provider (GCP/AWS for now).
+ *
+ * If this preprocessor is set then you have to also set [`cloudUploadOptions`](https://docs.openwa.dev/docs/reference/api/model/config/interfaces/ConfigObject#clouduploadoptions) in the config.
+ *
+ */
+ PREPROCESSORS["UPLOAD_CLOUD"] = "UPLOAD_CLOUD";
+})(PREPROCESSORS = exports.PREPROCESSORS || (exports.PREPROCESSORS = {}));
diff --git a/src/utils/pid_utils.js b/src/utils/pid_utils.js
new file mode 100644
index 0000000000..f038ee7a62
--- /dev/null
+++ b/src/utils/pid_utils.js
@@ -0,0 +1,60 @@
+"use strict";
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+exports.__esModule = true;
+exports.pidTreeUsage = void 0;
+var pidtree_1 = require("pidtree");
+var pidusage_1 = require("pidusage");
+var pidTreeUsage = function (pid) { return __awaiter(void 0, void 0, void 0, function () {
+ var pids, stats;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!Array.isArray(pid)) {
+ pid = [pid];
+ }
+ return [4 /*yield*/, Promise.all(pid.map(pidtree_1["default"]))];
+ case 1:
+ pids = (_a.sent()).flat();
+ return [4 /*yield*/, (0, pidusage_1["default"])(pids)];
+ case 2:
+ stats = _a.sent();
+ return [2 /*return*/, stats];
+ }
+ });
+}); };
+exports.pidTreeUsage = pidTreeUsage;
diff --git a/src/utils/tools.js b/src/utils/tools.js
new file mode 100644
index 0000000000..ee62b4cb82
--- /dev/null
+++ b/src/utils/tools.js
@@ -0,0 +1,595 @@
+"use strict";
+var __assign = (this && this.__assign) || function () {
+ __assign = Object.assign || function(t) {
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments[i];
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+ t[p] = s[p];
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+};
+var __rest = (this && this.__rest) || function (s, e) {
+ var t = {};
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
+ t[p] = s[p];
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
+ t[p[i]] = s[p[i]];
+ }
+ return t;
+};
+exports.__esModule = true;
+exports.sanitizeAccentedChars = exports.fixPath = exports.pathExists = exports.assertFile = exports.rmFileAsync = exports.FileOutputTypes = exports.FileInputTypes = exports.ensureDUrl = exports.generateGHIssueLink = exports.processSendData = exports.timePromise = exports.now = exports.perf = exports.processSend = exports.base64MimeType = exports.getDUrl = exports.getBufferFromUrl = exports.isDataURL = exports.isBase64 = exports.camelize = exports.without = exports.getConfigFromProcessEnv = exports.smartUserAgent = exports.timeout = void 0;
+var crypto_1 = require("crypto");
+var fs = require("fs");
+var fsp = require("fs/promises");
+var path = require("path");
+var datauri_1 = require("datauri");
+var is_url_superb_1 = require("is-url-superb");
+var model_1 = require("../api/model");
+var axios_1 = require("axios");
+var child_process_1 = require("child_process");
+var os_1 = require("os");
+var perf_hooks_1 = require("perf_hooks");
+var mime_1 = require("mime");
+var os_2 = require("os");
+var stream_1 = require("stream");
+var logging_1 = require("../logging/logging");
+var import_1 = require("@brillout/import");
+var fsconstants = fsp.constants || {
+ F_OK: 0,
+ R_OK: 4,
+ W_OK: 2,
+ X_OK: 1
+};
+var IGNORE_FILE_EXTS = [
+ 'mpga'
+];
+var _ft = null;
+var ft = function () { return __awaiter(void 0, void 0, void 0, function () {
+ var x;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!!_ft) return [3 /*break*/, 2];
+ return [4 /*yield*/, (0, import_1.import_)('file-type')];
+ case 1:
+ x = _a.sent();
+ _ft = x;
+ _a.label = 2;
+ case 2: return [2 /*return*/, _ft];
+ }
+ });
+}); };
+//@ts-ignore
+process.send = process.send || function () { };
+var timeout = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms, 'timeout'); }); };
+exports.timeout = timeout;
+/**
+ * Use this to generate a more likely valid user agent. It makes sure it has the WA part and replaces any windows or linux os info with mac.
+ * @param useragent Your custom user agent
+ * @param v The WA version from the debug info. This is optional. default is 2.2117.5
+ */
+var smartUserAgent = function (useragent, v) {
+ if (v === void 0) { v = '2.2117.5'; }
+ useragent = useragent.replace(useragent
+ .match(/\(([^()]*)\)/g)
+ .find(function (x) {
+ return x.toLowerCase().includes('linux') ||
+ x.toLowerCase().includes('windows');
+ }), '(Macintosh; Intel Mac OS X 10_15_2)');
+ if (!useragent.includes('WhatsApp'))
+ return "WhatsApp/".concat(v, " ").concat(useragent);
+ return useragent.replace(useragent
+ .match(/WhatsApp\/([.\d])*/g)[0]
+ .match(/[.\d]*/g)
+ .find(function (x) { return x; }), v);
+};
+exports.smartUserAgent = smartUserAgent;
+var getConfigFromProcessEnv = function (json) {
+ var output = {};
+ json.forEach(function (_a) {
+ var env = _a.env, key = _a.key;
+ if (process.env[env])
+ output[key] = process.env[env];
+ if (process.env[env] === 'true' || process.env[env] === 'false')
+ output[key] = Boolean(process.env[env]);
+ });
+ return output;
+};
+exports.getConfigFromProcessEnv = getConfigFromProcessEnv;
+/**
+ * Remove the key from the object and return the rest of the object.
+ * @param {JsonObject} obj - The object to be filtered.
+ * @param {string} key - The key to discard.
+ * @returns The object without the key.
+ */
+var without = function (obj, key) {
+ var _a = obj,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ _b = key,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ discard = _a[_b], rest = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]);
+ return rest;
+};
+exports.without = without;
+var camelize = function (str) {
+ var arr = str.split('-');
+ var capital = arr.map(function (item, index) {
+ return index
+ ? item.charAt(0).toUpperCase() + item.slice(1).toLowerCase()
+ : item.toLowerCase();
+ });
+ // ^-- change here.
+ var capitalString = capital.join('');
+ return capitalString;
+};
+exports.camelize = camelize;
+/**
+ * Check if a string is Base64
+ * @param str string
+ * @returns
+ */
+var isBase64 = function (str) {
+ var len = str.length;
+ if (!len || len % 4 !== 0 || /[^A-Z0-9+/=]/i.test(str)) {
+ return false;
+ }
+ var firstPaddingChar = str.indexOf('=');
+ return (firstPaddingChar === -1 ||
+ firstPaddingChar === len - 1 ||
+ (firstPaddingChar === len - 2 && str[len - 1] === '='));
+};
+exports.isBase64 = isBase64;
+/**
+ * Check if a string is a DataURL
+ * @param s string
+ * @returns
+ */
+var isDataURL = function (s) {
+ return !!s.match(/^data:((?:\w+\/(?:(?!;).)+)?)((?:;[\w\W]*?[^;])*),(.+)$/g);
+};
+exports.isDataURL = isDataURL;
+/**
+ * @internal
+ * A convinience method to download the buffer of a downloaded file
+ * @param url The url
+ * @param optionsOverride You can use this to override the [axios request config](https://github.com/axios/axios#request-config)
+ */
+var getBufferFromUrl = function (url, optionsOverride) {
+ if (optionsOverride === void 0) { optionsOverride = {}; }
+ return __awaiter(void 0, void 0, void 0, function () {
+ var res, error_1;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _a.trys.push([0, 2, , 3]);
+ return [4 /*yield*/, (0, axios_1["default"])(__assign(__assign({ method: 'get', url: url, headers: {
+ DNT: 1,
+ 'Upgrade-Insecure-Requests': 1
+ } }, optionsOverride), { responseType: 'arraybuffer' }))];
+ case 1:
+ res = _a.sent();
+ return [2 /*return*/, [Buffer.from(res.data, 'binary'), res.headers]];
+ case 2:
+ error_1 = _a.sent();
+ throw error_1;
+ case 3: return [2 /*return*/];
+ }
+ });
+ });
+};
+exports.getBufferFromUrl = getBufferFromUrl;
+/**
+ * @internal
+ * A convinience method to download the [[DataURL]] of a file
+ * @param url The url
+ * @param optionsOverride You can use this to override the [axios request config](https://github.com/axios/axios#request-config)
+ */
+var getDUrl = function (url, optionsOverride) {
+ if (optionsOverride === void 0) { optionsOverride = {}; }
+ return __awaiter(void 0, void 0, void 0, function () {
+ var _a, buff, headers, dUrl, error_2;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ _b.trys.push([0, 2, , 3]);
+ return [4 /*yield*/, (0, exports.getBufferFromUrl)(url, optionsOverride)];
+ case 1:
+ _a = _b.sent(), buff = _a[0], headers = _a[1];
+ dUrl = "data:".concat(headers['content-type'], ";base64,").concat(buff.toString('base64'));
+ return [2 /*return*/, dUrl];
+ case 2:
+ error_2 = _b.sent();
+ throw error_2;
+ case 3: return [2 /*return*/];
+ }
+ });
+ });
+};
+exports.getDUrl = getDUrl;
+/**
+ * @internal
+ * Use this to extract the mime type from a [[DataURL]]
+ */
+var base64MimeType = function (dUrl) {
+ var result = null;
+ if (typeof dUrl !== 'string') {
+ return result;
+ }
+ var mime = dUrl.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/);
+ if (mime && mime.length) {
+ result = mime[1];
+ }
+ return result;
+};
+exports.base64MimeType = base64MimeType;
+/**
+ * If process.send is defined, send the message three times
+ * @param {string} message - The message to send to the parent process.
+ * @returns Nothing.
+ */
+var processSend = function (message) {
+ if (process.send) {
+ process.send(message);
+ process.send(message);
+ process.send(message);
+ }
+ return;
+};
+exports.processSend = processSend;
+/**
+ * Return the performance object if it is available, otherwise return the Date object
+ */
+var perf = function () { return perf_hooks_1.performance || Date; };
+exports.perf = perf;
+/**
+ * Return the current time in milliseconds
+ */
+var now = function () { return (0, exports.perf)().now(); };
+exports.now = now;
+/**
+ * `timePromise` returns a promise that resolves to the time it took to run the function passed to it
+ * @param fn - the function to be timed.
+ * @returns A string.
+ */
+function timePromise(fn) {
+ return __awaiter(this, void 0, void 0, function () {
+ var start;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ start = (0, exports.now)();
+ return [4 /*yield*/, fn()];
+ case 1:
+ _a.sent();
+ return [2 /*return*/, ((0, exports.now)() - start).toFixed(0)];
+ }
+ });
+ });
+}
+exports.timePromise = timePromise;
+/**
+ * It sends a message to the parent process.
+ * @param {any} data - The data to be sent to the parent process.
+ * @returns Nothing.
+ */
+var processSendData = function (data) {
+ if (data === void 0) { data = {}; }
+ var sd = function () { return process.send({
+ type: 'process:msg',
+ data: data
+ }, function (error) {
+ if (error) {
+ console.error(error);
+ }
+ }); };
+ return sd();
+ // return await new Promise((resolve, reject)=>{
+ // sd(resolve,reject)
+ // })
+};
+exports.processSendData = processSendData;
+/**
+ * It generates a link to the GitHub issue template for the current session
+ * @param {ConfigObject} config - the config object
+ * @param {SessionInfo} sessionInfo - The sessionInfo object from the CLI
+ * @param {any} extras - any
+ * @returns A link to the issue tracker for the current session.
+ */
+var generateGHIssueLink = function (config, sessionInfo, extras) {
+ if (extras === void 0) { extras = {}; }
+ var npm_ver = (0, child_process_1.execSync)('npm -v');
+ var labels = [];
+ if (sessionInfo.CLI)
+ labels.push('CLI');
+ if (!sessionInfo.LATEST_VERSION)
+ labels.push('NCV');
+ labels.push(config.multiDevice ? 'MD' : 'Legacy');
+ if (sessionInfo.ACC_TYPE === 'BUSINESS')
+ labels.push('BHA');
+ if (sessionInfo.ACC_TYPE === 'PERSONAL')
+ labels.push('PHA');
+ var qp = __assign({ "template": "bug_report.yaml",
+ //@ts-ignore
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ "d_info": "".concat(encodeURI(JSON.stringify((function (_a) {
+ var OS = _a.OS, purged = _a.purged, PAGE_UA = _a.PAGE_UA, OW_KEY = _a.OW_KEY, NUM = _a.NUM, NUM_HASH = _a.NUM_HASH, o = __rest(_a, ["OS", "purged", "PAGE_UA", "OW_KEY", "NUM", "NUM_HASH"]);
+ return o;
+ })(sessionInfo), null, 2))), "enviro": "".concat("-%20OS:%20".concat(encodeURI(sessionInfo.OS), "%0A-%20Node:%20").concat(encodeURI(process.versions.node), "%0A-%20npm:%20").concat((String(npm_ver)).replace(/\s/g, ''))), "labels": labels.join(',') }, extras);
+ return "https://github.com/open-wa/wa-automate-nodejs/issues/new?".concat(Object.keys(qp).map(function (k) { return "".concat(k, "=").concat(qp[k]); }).join('&'));
+};
+exports.generateGHIssueLink = generateGHIssueLink;
+/**
+ * If the file is a DataURL, return it. If it's a file, convert it to a DataURL. If it's a URL,
+ * download it and convert it to a DataURL. If Base64, returns it.
+ * @param {string} file - The file to be converted to a DataURL.
+ * @param {AxiosRequestConfig} requestConfig - AxiosRequestConfig = {}
+ * @param {string} filename - Filename with an extension so a datauri mimetype can be inferred.
+ * @returns A DataURL
+ */
+var ensureDUrl = function (file, requestConfig, filename) {
+ if (requestConfig === void 0) { requestConfig = {}; }
+ return __awaiter(void 0, void 0, void 0, function () {
+ var ext, relativePath, ext;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ if (!Buffer.isBuffer(file)) return [3 /*break*/, 4];
+ if (!!filename) return [3 /*break*/, 3];
+ return [4 /*yield*/, ft()];
+ case 1: return [4 /*yield*/, (_a.sent()).fileTypeFromBuffer(file)];
+ case 2:
+ ext = (_a.sent()).ext;
+ filename = "file.".concat(ext);
+ _a.label = 3;
+ case 3: return [2 /*return*/, "data:".concat(mime_1["default"].getType(filename), ";base64,").concat(file.toString('base64').split(',')[1])];
+ case 4:
+ if (!(!(0, exports.isDataURL)(file) && !(0, exports.isBase64)(file))) return [3 /*break*/, 9];
+ relativePath = path.join(path.resolve(process.cwd(), file || ''));
+ if (!(fs.existsSync(file) || fs.existsSync(relativePath))) return [3 /*break*/, 6];
+ return [4 /*yield*/, (0, datauri_1["default"])(fs.existsSync(file) ? file : relativePath)];
+ case 5:
+ file = _a.sent();
+ return [3 /*break*/, 9];
+ case 6:
+ if (!(0, is_url_superb_1["default"])(file)) return [3 /*break*/, 8];
+ return [4 /*yield*/, (0, exports.getDUrl)(file, requestConfig)];
+ case 7:
+ file = _a.sent();
+ return [3 /*break*/, 9];
+ case 8: throw new model_1.CustomError(model_1.ERROR_NAME.FILE_NOT_FOUND, 'Cannot find file. Make sure the file reference is relative, a valid URL or a valid DataURL');
+ case 9:
+ if (!!filename) return [3 /*break*/, 12];
+ return [4 /*yield*/, ft()];
+ case 10: return [4 /*yield*/, (_a.sent()).fileTypeFromBuffer(Buffer.from(file.split(',')[1], 'base64'))];
+ case 11:
+ ext = (_a.sent()).ext;
+ filename = "file.".concat(ext);
+ _a.label = 12;
+ case 12:
+ if (file.includes("data:") && file.includes("undefined") || file.includes("application/octet-stream") && filename && mime_1["default"].getType(filename)) {
+ file = "data:".concat(mime_1["default"].getType(filename), ";base64,").concat(file.split(',')[1]);
+ }
+ return [2 /*return*/, file];
+ }
+ });
+ });
+};
+exports.ensureDUrl = ensureDUrl;
+exports.FileInputTypes = {
+ "VALIDATED_FILE_PATH": "VALIDATED_FILE_PATH",
+ "URL": "URL",
+ "DATA_URL": "DATA_URL",
+ "BASE_64": "BASE_64",
+ "BUFFER": "BUFFER",
+ "READ_STREAM": "READ_STREAM"
+};
+exports.FileOutputTypes = __assign(__assign({}, exports.FileInputTypes), { "TEMP_FILE_PATH": "TEMP_FILE_PATH" });
+/**
+ * Remove file asynchronously
+ * @param file Filepath
+ * @returns
+ */
+function rmFileAsync(file) {
+ return new Promise(function (resolve, reject) {
+ fs.unlink(file, function (err) {
+ if (err) {
+ reject(err);
+ }
+ else {
+ resolve(true);
+ }
+ });
+ });
+}
+exports.rmFileAsync = rmFileAsync;
+/**
+ * Takes a file parameter and consistently returns the desired type of file.
+ * @param file The file path, URL, base64 or DataURL string of the file
+ * @param outfileName The ouput filename of the file
+ * @param desiredOutputType The type of file output required from this function
+ * @param requestConfig optional axios config if file parameter is a url
+ */
+var assertFile = function (file, outfileName, desiredOutputType, requestConfig) { return __awaiter(void 0, void 0, void 0, function () {
+ var inputType, relativePath, _a, tfn, ext, tempFilePath, _b, _c, _d, _e;
+ return __generator(this, function (_f) {
+ switch (_f.label) {
+ case 0:
+ outfileName = (0, exports.sanitizeAccentedChars)(outfileName);
+ if (typeof file == 'string') {
+ if ((0, exports.isDataURL)(file))
+ inputType = exports.FileInputTypes.DATA_URL;
+ else if ((0, exports.isBase64)(file))
+ inputType = exports.FileInputTypes.BASE_64;
+ /**
+ * Check if it is a path
+ */
+ else {
+ relativePath = path.join(path.resolve(process.cwd(), file || ''));
+ if (fs.existsSync(file) || fs.existsSync(relativePath)) {
+ // file = await datauri(fs.existsSync(file) ? file : relativePath);
+ inputType = exports.FileInputTypes.VALIDATED_FILE_PATH;
+ }
+ else if ((0, is_url_superb_1["default"])(file))
+ inputType = exports.FileInputTypes.URL;
+ /**
+ * If not file type is determined by now then it is some sort of unidentifiable string. Throw an error.
+ */
+ if (!inputType)
+ throw new model_1.CustomError(model_1.ERROR_NAME.FILE_NOT_FOUND, "Cannot find file. Make sure the file reference is relative, a valid URL or a valid DataURL: ".concat(file.slice(0, 25)));
+ }
+ }
+ else {
+ if (Buffer.isBuffer(file))
+ inputType = exports.FileInputTypes.BUFFER;
+ /**
+ * Leave space to determine if incoming file parameter is any other type of object (maybe one day people will submit a path object as a param?)
+ */
+ }
+ if (inputType === desiredOutputType)
+ return [2 /*return*/, file];
+ _a = desiredOutputType;
+ switch (_a) {
+ case exports.FileOutputTypes.DATA_URL: return [3 /*break*/, 1];
+ case exports.FileOutputTypes.BASE_64: return [3 /*break*/, 1];
+ case exports.FileOutputTypes.TEMP_FILE_PATH: return [3 /*break*/, 3];
+ case exports.FileOutputTypes.BUFFER: return [3 /*break*/, 7];
+ case exports.FileOutputTypes.READ_STREAM: return [3 /*break*/, 9];
+ }
+ return [3 /*break*/, 13];
+ case 1: return [4 /*yield*/, (0, exports.ensureDUrl)(file, requestConfig, outfileName)];
+ case 2: return [2 /*return*/, _f.sent()];
+ case 3:
+ tfn = "".concat(crypto_1["default"].randomBytes(6).readUIntLE(0, 6).toString(36), ".").concat(outfileName);
+ if (!(inputType != exports.FileInputTypes.BUFFER)) return [3 /*break*/, 5];
+ return [4 /*yield*/, (0, exports.ensureDUrl)(file, requestConfig, outfileName)];
+ case 4:
+ file = _f.sent();
+ ext = mime_1["default"].getExtension(file.match(/[^:]\w+\/[\w-+\d.]+(?=;|,)/)[0]);
+ if (ext && !IGNORE_FILE_EXTS.includes(ext) && !tfn.endsWith(ext))
+ tfn = "".concat(tfn, ".").concat(ext);
+ file = Buffer.from(file.split(',')[1], 'base64');
+ _f.label = 5;
+ case 5:
+ tempFilePath = path.join((0, os_2.tmpdir)(), tfn);
+ return [4 /*yield*/, fs.writeFileSync(tempFilePath, file)];
+ case 6:
+ _f.sent();
+ logging_1.log.info("Saved temporary file to ".concat(tempFilePath));
+ return [2 /*return*/, tempFilePath];
+ case 7:
+ _c = (_b = Buffer).from;
+ return [4 /*yield*/, (0, exports.ensureDUrl)(file, requestConfig, outfileName)];
+ case 8: return [2 /*return*/, _c.apply(_b, [(_f.sent()).split(',')[1], 'base64'])];
+ case 9:
+ if (!(inputType === exports.FileInputTypes.VALIDATED_FILE_PATH)) return [3 /*break*/, 10];
+ return [2 /*return*/, fs.createReadStream(file)];
+ case 10:
+ if (!(inputType != exports.FileInputTypes.BUFFER)) return [3 /*break*/, 12];
+ _e = (_d = Buffer).from;
+ return [4 /*yield*/, (0, exports.ensureDUrl)(file, requestConfig, outfileName)];
+ case 11:
+ file = _e.apply(_d, [(_f.sent()).split(',')[1], 'base64']);
+ _f.label = 12;
+ case 12: return [2 /*return*/, stream_1.Readable.from(file)];
+ case 13: return [2 /*return*/, file];
+ }
+ });
+}); };
+exports.assertFile = assertFile;
+/**
+ * Checks if a given path exists.
+ *
+ * If exists, returns the resolved absolute path. Otherwise returns false.
+ *
+ * @param _path a relative, absolute or homedir path to a folder or a file
+ * @param failSilent If you're expecting for the file to not exist and just want the `false` response then set this to true to prevent false-positive error messages in the logs.
+ * @returns string | false
+ */
+var pathExists = function (_path, failSilent) { return __awaiter(void 0, void 0, void 0, function () {
+ var error_3;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ _path = (0, exports.fixPath)(_path);
+ _a.label = 1;
+ case 1:
+ _a.trys.push([1, 3, , 4]);
+ return [4 /*yield*/, fsp.access(_path, fsconstants.R_OK | fsconstants.W_OK)];
+ case 2:
+ _a.sent();
+ return [2 /*return*/, _path];
+ case 3:
+ error_3 = _a.sent();
+ if (!failSilent)
+ logging_1.log.error('Given check path threw an error', error_3);
+ return [2 /*return*/, false];
+ case 4: return [2 /*return*/];
+ }
+ });
+}); };
+exports.pathExists = pathExists;
+/**
+ * Returns an absolute file path reference
+ * @param _path a relative, absolute or homedir path to a folder or a file
+ * @returns string
+ */
+var fixPath = function (_path) {
+ _path = _path.replace("~", (0, os_1.homedir)());
+ _path = _path.includes('./') ? path.join(process.cwd(), _path) : _path;
+ return _path;
+};
+exports.fixPath = fixPath;
+/**
+ *
+ * Accented filenames break file sending in docker containers. This is used to replace accented chars in strings to prevent file sending failures.
+ *
+ * @param input The raw string
+ * @returns A sanitized string with all accented chars removed
+ */
+var sanitizeAccentedChars = function (input) {
+ return input
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '');
+};
+exports.sanitizeAccentedChars = sanitizeAccentedChars;
diff --git a/test_qr.png b/test_qr.png
new file mode 100644
index 0000000000..4e9846a7b7
Binary files /dev/null and b/test_qr.png differ
diff --git a/test_qr_events.js b/test_qr_events.js
new file mode 100644
index 0000000000..9dab0ddc7b
--- /dev/null
+++ b/test_qr_events.js
@@ -0,0 +1,21 @@
+const { ev } = require('./dist/index.js');
+
+console.log('Testing QR event listener...');
+
+// 监听所有事件来调试
+ev.on('**', (data, sessionId, namespace) => {
+ console.log(`EVENT: ${namespace}, Session: ${sessionId}, Data: ${typeof data === 'string' ? data.substring(0, 50) : typeof data}`);
+});
+
+// 专门监听QR码事件
+ev.on('qr.**', (qrcode) => {
+ console.log('QR EVENT CAUGHT!');
+ console.log('QR length:', qrcode ? qrcode.length : 'null');
+});
+
+console.log('Event listeners set up. Monitoring for 60 seconds...');
+
+setTimeout(() => {
+ console.log('Test completed');
+ process.exit(0);
+}, 60000);
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 8fc498dc2f..35fe4cb872 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,57 +1,16 @@
{
"compilerOptions": {
- /* Basic Options */
- "target": "ES2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
- "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
- // "lib": [], /* Specify library files to be included in the compilation. */
- // "allowJs": true, /* Allow javascript files to be compiled. */
- // "checkJs": true, /* Report errors in .js files. */
- // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
- "declaration": true /* Generates corresponding '.d.ts' file. */,
- // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
- // "sourceMap": true, /* Generates corresponding '.map' file. */
- // "outFile": "./", /* Concatenate and emit output to single file. */
- "outDir": "./dist" /* Redirect output structure to the directory. */,
- // "rootDir": "./src/", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
- // "composite": true, /* Enable project compilation */
- "removeComments": false /* Do not emit comments to output. */,
-
+ "target": "ES2015",
+ "module": "commonjs",
+ "declaration": true,
+ "outDir": "./dist",
+ "removeComments": false,
"skipLibCheck": true,
- // "noEmit": true, /* Do not emit outputs. */
- // "importHelpers": true, /* Import emit helpers from 'tslib'. */
- // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
- // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
- /* Strict Type-Checking Options */
- // "strict": true, /* Enable all strict type-checking options. */
- // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
- // "strictNullChecks": true, /* Enable strict null checks. */
- // "strictFunctionTypes": true, /* Enable strict checking of function types. */
- // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
- // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
- // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
- /* Additional Checks */
- // "noUnusedLocals": true, /* Report errors on unused locals. */
- // "noUnusedParameters": true, /* Report errors on unused parameters. */
- // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
- // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
- /* Module Resolution Options */
- // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
- // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
- // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
- // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
- // "typeRoots": [], /* List of folders to include type definitions from. */
- // "types": [], /* Type declaration files to be included in compilation. */
- "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
- "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
- // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
- /* Source Map Options */
- // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
- // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
- // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
- // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
- /* Experimental Options */
- // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
- // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "inlineSourceMap": true,
+ "inlineSources": true,
+ "declarationMap": true
},
"include": [
"src/**/*"
diff --git a/web-ui/assets/css/style.css b/web-ui/assets/css/style.css
new file mode 100644
index 0000000000..17cbca70e5
--- /dev/null
+++ b/web-ui/assets/css/style.css
@@ -0,0 +1,611 @@
+/* 全局样式 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+ color: #333;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+/* 头部样式 */
+.header {
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(10px);
+ border-radius: 12px;
+ padding: 20px;
+ margin-bottom: 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.logo i {
+ font-size: 2rem;
+ color: #25d366;
+}
+
+.logo h1 {
+ font-size: 1.8rem;
+ font-weight: 600;
+ color: #333;
+}
+
+.server-status {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-weight: 500;
+}
+
+.status-indicator {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ display: inline-block;
+}
+
+.status-indicator.online {
+ background: #22c55e;
+ box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2);
+}
+
+.status-indicator.offline {
+ background: #ef4444;
+ box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
+}
+
+/* 主要内容区域 */
+.main-content {
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(10px);
+ border-radius: 12px;
+ padding: 20px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+}
+
+/* 工具栏 */
+.toolbar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.search-box {
+ position: relative;
+ max-width: 300px;
+ flex: 1;
+}
+
+.search-box input {
+ width: 100%;
+ padding: 10px 40px 10px 12px;
+ border: 2px solid #e5e7eb;
+ border-radius: 8px;
+ font-size: 14px;
+ transition: border-color 0.3s;
+}
+
+.search-box input:focus {
+ outline: none;
+ border-color: #667eea;
+}
+
+.search-box i {
+ position: absolute;
+ right: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ color: #9ca3af;
+}
+
+/* 按钮样式 */
+.btn {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.btn-primary {
+ background: #667eea;
+ color: white;
+}
+
+.btn-primary:hover {
+ background: #5a67d8;
+ transform: translateY(-1px);
+}
+
+.btn-secondary {
+ background: #f3f4f6;
+ color: #374151;
+}
+
+.btn-secondary:hover {
+ background: #e5e7eb;
+}
+
+.btn-success {
+ background: #10b981;
+ color: white;
+}
+
+.btn-success:hover {
+ background: #059669;
+}
+
+.btn-danger {
+ background: #ef4444;
+ color: white;
+}
+
+.btn-danger:hover {
+ background: #dc2626;
+}
+
+.btn-warning {
+ background: #f59e0b;
+ color: white;
+}
+
+.btn-warning:hover {
+ background: #d97706;
+}
+
+.btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+/* 会话容器 */
+.sessions-container {
+ margin-top: 20px;
+}
+
+.sessions-header h2 {
+ margin-bottom: 15px;
+ color: #374151;
+}
+
+.sessions-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+ gap: 20px;
+}
+
+/* 会话卡片 */
+.session-card {
+ background: white;
+ border-radius: 12px;
+ padding: 20px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+ border: 1px solid #e5e7eb;
+ transition: all 0.3s;
+}
+
+.session-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
+}
+
+.session-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 15px;
+}
+
+.session-title {
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: #111827;
+}
+
+.session-status {
+ padding: 4px 8px;
+ border-radius: 6px;
+ font-size: 12px;
+ font-weight: 500;
+ text-transform: uppercase;
+}
+
+.status-ready {
+ background: #dcfce7;
+ color: #166534;
+}
+
+.status-initializing {
+ background: #fef3c7;
+ color: #92400e;
+}
+
+.status-qr_ready {
+ background: #dbeafe;
+ color: #1e40af;
+}
+
+.status-failed {
+ background: #fee2e2;
+ color: #991b1b;
+}
+
+.status-terminated {
+ background: #f3f4f6;
+ color: #374151;
+}
+
+.session-info {
+ margin-bottom: 15px;
+}
+
+.session-info-item {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 8px;
+ font-size: 14px;
+}
+
+.session-info-label {
+ color: #6b7280;
+}
+
+.session-info-value {
+ color: #111827;
+ font-weight: 500;
+}
+
+.session-actions {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.session-actions .btn {
+ padding: 8px 12px;
+ font-size: 12px;
+}
+
+/* 更多操作菜单 */
+.more-actions {
+ position: relative;
+ display: inline-block;
+}
+
+.dropdown-menu {
+ display: none;
+ position: absolute;
+ right: 0;
+ background-color: white;
+ min-width: 160px;
+ box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+ z-index: 1;
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.dropdown-item {
+ width: 100%;
+ text-align: left;
+ background: none;
+ border: none;
+ padding: 12px 16px;
+ cursor: pointer;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ color: #374151;
+}
+
+.dropdown-item:hover {
+ background-color: #f3f4f6;
+}
+
+.dropdown-item.danger {
+ color: #dc2626;
+}
+
+.dropdown-item.danger:hover {
+ background-color: #fee2e2;
+}
+
+.dropdown-divider {
+ height: 1px;
+ background-color: #e5e7eb;
+ margin: 8px 0;
+}
+
+/* 模态框样式 */
+.modal {
+ display: none;
+ position: fixed;
+ z-index: 1000;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+ backdrop-filter: blur(4px);
+}
+
+.modal-content {
+ background-color: white;
+ margin: 5% auto;
+ border-radius: 12px;
+ width: 90%;
+ max-width: 500px;
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
+ animation: modalSlideIn 0.3s ease-out;
+}
+
+.qr-modal .modal-content {
+ max-width: 400px;
+}
+
+@keyframes modalSlideIn {
+ from {
+ opacity: 0;
+ transform: translateY(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.modal-header {
+ padding: 20px;
+ border-bottom: 1px solid #e5e7eb;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.modal-header h3 {
+ margin: 0;
+ color: #111827;
+}
+
+.close {
+ color: #9ca3af;
+ font-size: 28px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: color 0.3s;
+}
+
+.close:hover {
+ color: #374151;
+}
+
+.modal-body {
+ padding: 20px;
+}
+
+.modal-footer {
+ padding: 20px;
+ border-top: 1px solid #e5e7eb;
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+/* 表单样式 */
+.form-group {
+ margin-bottom: 15px;
+}
+
+.form-group label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: 500;
+ color: #374151;
+}
+
+.form-group input[type="text"],
+.form-group input[type="password"],
+.form-group select {
+ width: 100%;
+ padding: 10px 12px;
+ border: 2px solid #e5e7eb;
+ border-radius: 8px;
+ font-size: 14px;
+ transition: border-color 0.3s;
+}
+
+.form-group input[type="text"]:focus,
+.form-group input[type="password"]:focus,
+.form-group select:focus {
+ outline: none;
+ border-color: #667eea;
+}
+
+.form-group input[type="checkbox"] {
+ margin-right: 8px;
+}
+
+/* QR码容器 */
+.qr-container {
+ text-align: center;
+}
+
+.qr-code {
+ background: #f9fafb;
+ border: 2px dashed #d1d5db;
+ border-radius: 12px;
+ padding: 30px;
+ margin-bottom: 20px;
+ min-height: 200px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.qr-loading {
+ text-align: center;
+ color: #6b7280;
+}
+
+.qr-loading i {
+ font-size: 2rem;
+ margin-bottom: 10px;
+}
+
+.qr-image {
+ max-width: 100%;
+ height: auto;
+}
+
+.qr-instructions {
+ color: #6b7280;
+ font-size: 14px;
+}
+
+.qr-instructions i {
+ color: #667eea;
+ margin-right: 5px;
+}
+
+/* Toast 消息 */
+.toast-container {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ z-index: 1100;
+}
+
+.toast {
+ background: white;
+ border-radius: 8px;
+ padding: 15px;
+ margin-bottom: 10px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ border-left: 4px solid #667eea;
+ animation: toastSlideIn 0.3s ease-out;
+ min-width: 300px;
+}
+
+.toast.success {
+ border-left-color: #10b981;
+}
+
+.toast.error {
+ border-left-color: #ef4444;
+}
+
+.toast.warning {
+ border-left-color: #f59e0b;
+}
+
+@keyframes toastSlideIn {
+ from {
+ opacity: 0;
+ transform: translateX(100%);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .container {
+ padding: 10px;
+ }
+
+ .header {
+ flex-direction: column;
+ gap: 15px;
+ text-align: center;
+ }
+
+ .toolbar {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .search-box {
+ max-width: none;
+ }
+
+ .sessions-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .modal-content {
+ margin: 10% auto;
+ width: 95%;
+ }
+
+ .session-actions {
+ justify-content: center;
+ }
+}
+
+/* 空状态 */
+.empty-state {
+ text-align: center;
+ padding: 60px 20px;
+ color: #6b7280;
+}
+
+.empty-state i {
+ font-size: 4rem;
+ margin-bottom: 20px;
+ color: #d1d5db;
+}
+
+.empty-state h3 {
+ margin-bottom: 10px;
+ color: #374151;
+}
+
+.empty-state p {
+ margin-bottom: 20px;
+}
+
+.proxy-test-result {
+ margin-top: 15px;
+ padding: 10px;
+ border-radius: 6px;
+ font-weight: 500;
+ display: none; /* 默认隐藏 */
+}
+
+.proxy-test-result.success {
+ background-color: #dcfce7;
+ color: #166534;
+}
+
+.proxy-test-result.error {
+ background-color: #fee2e2;
+ color: #991b1b;
+}
\ No newline at end of file
diff --git a/web-ui/assets/js/app.js b/web-ui/assets/js/app.js
new file mode 100644
index 0000000000..f9072dc311
--- /dev/null
+++ b/web-ui/assets/js/app.js
@@ -0,0 +1,823 @@
+// API 基础地址
+const API_BASE = '/api/v1';
+
+// 全局变量
+let sessions = [];
+let currentSessionId = null;
+
+// 页面加载完成后初始化
+document.addEventListener('DOMContentLoaded', function() {
+ loadSessions();
+ checkServerStatus();
+
+ // 定期刷新会话状态
+ setInterval(loadSessions, 10000); // 每10秒刷新一次
+ setInterval(checkServerStatus, 5000); // 每5秒检查服务器状态
+});
+
+// 检查服务器状态
+async function checkServerStatus() {
+ try {
+ const response = await fetch(`${API_BASE}/sessions`);
+ const statusIndicator = document.getElementById('serverStatus');
+
+ if (response.ok) {
+ statusIndicator.className = 'status-indicator online';
+ } else {
+ statusIndicator.className = 'status-indicator offline';
+ }
+ } catch (error) {
+ document.getElementById('serverStatus').className = 'status-indicator offline';
+ }
+}
+
+// 加载所有会话
+async function loadSessions() {
+ try {
+ const response = await fetch(`${API_BASE}/sessions`, {
+ cache: 'no-cache' // 强制从服务器获取最新数据
+ });
+ const data = await response.json();
+
+ // 确保能正确处理后端直接返回的会话数组
+ sessions = Array.isArray(data.data) ? data.data : [];
+
+ renderSessions();
+ updateSessionCount();
+
+ } catch (error) {
+ console.error('加载会话失败:', error);
+ showToast('无法连接到服务器', 'error');
+ }
+}
+
+// 渲染会话列表
+function renderSessions() {
+ const container = document.getElementById('sessionsGrid');
+
+ if (sessions.length === 0) {
+ container.innerHTML = `
+
+
+
暂无会话
+
点击上方的"新建会话"按钮来创建您的第一个会话
+
+
+ `;
+ return;
+ }
+
+ container.innerHTML = sessions.map(session => createSessionCard(session)).join('');
+}
+
+// 创建会话卡片HTML
+function createSessionCard(session) {
+ const createdAt = new Date(session.createdAt).toLocaleString('zh-CN');
+ const lastActivity = new Date(session.lastActivity).toLocaleString('zh-CN');
+
+ return `
+
+
+
+
+
+ 代理:
+ ${getProxyDisplayText(session.config)}
+
+
+ 创建时间:
+ ${createdAt}
+
+
+ 最后活动:
+ ${lastActivity}
+
+
+ 状态:
+ ${getStatusText(session.status)}
+
+
+
+
+ ${getSessionActions(session)}
+
+
+ `;
+}
+
+// 获取状态文本
+function getStatusText(status) {
+ const statusMap = {
+ 'initializing': '初始化中',
+ 'qr_ready': '待扫码',
+ 'authenticated': '已认证',
+ 'ready': '已就绪',
+ 'failed': '失败',
+ 'terminated': '已终止'
+ };
+ return statusMap[status] || status;
+}
+
+// 获取代理显示文本
+function getProxyDisplayText(config) {
+ if (!config) return '无';
+
+ const proxy = config.proxyServerCredentials;
+ if (!proxy || !proxy.address) {
+ return '无';
+ }
+
+ // 显示代理地址,如果有用户名则显示用户名
+ let displayText = proxy.address;
+ if (proxy.username) {
+ displayText += ` (${proxy.username})`;
+ }
+
+ return displayText;
+}
+
+// 获取会话操作按钮
+function getSessionActions(session) {
+ let actions = [];
+
+ if (session.status === 'qr_ready') {
+ actions.push(``);
+ }
+
+ if (session.status === 'ready') {
+ actions.push(``);
+ actions.push(``);
+ }
+
+ actions.push(``);
+
+ // 将更多操作收进一个下拉菜单中,防止误触
+ actions.push(`
+
+
+
+
+ `);
+
+ return actions.join('');
+}
+
+// 切换更多操作菜单的显示
+function toggleMoreActions(btn) {
+ const menu = btn.nextElementSibling;
+ menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
+}
+
+// 点击页面其他地方关闭所有“更多操作”菜单
+document.addEventListener('click', function(event) {
+ if (!event.target.closest('.more-actions')) {
+ document.querySelectorAll('.dropdown-menu').forEach(menu => {
+ menu.style.display = 'none';
+ });
+ }
+});
+
+// 更新会话数量
+function updateSessionCount() {
+ document.getElementById('sessionCount').textContent = sessions.length;
+}
+
+// 搜索过滤会话
+function filterSessions() {
+ const searchTerm = document.getElementById('searchInput').value.toLowerCase();
+ const cards = document.querySelectorAll('.session-card');
+
+ cards.forEach(card => {
+ const sessionId = card.getAttribute('data-session-id').toLowerCase();
+ if (sessionId.includes(searchTerm)) {
+ card.style.display = 'block';
+ } else {
+ card.style.display = 'none';
+ }
+ });
+}
+
+// 显示创建会话模态框
+function showCreateSessionModal() {
+ document.getElementById('createSessionModal').style.display = 'block';
+ document.getElementById('newSessionId').value = '';
+ document.getElementById('waitForQR').checked = true;
+ // 重置代理字段
+ document.getElementById('enableProxy').checked = false;
+ document.getElementById('createProxyFields').style.display = 'none';
+ document.getElementById('createProxyHost').value = '';
+ document.getElementById('createProxyPort').value = '';
+ document.getElementById('createProxyUser').value = '';
+ document.getElementById('createProxyPass').value = '';
+ document.getElementById('createProxyTestResult').style.display = 'none';
+}
+
+// 切换创建会话模态框中的代理输入字段
+function toggleCreateProxyFields() {
+ const enableProxy = document.getElementById('enableProxy').checked;
+ document.getElementById('createProxyFields').style.display = enableProxy ? 'block' : 'none';
+}
+
+// 在创建会话模态框中测试代理
+async function testHttpProxy(type) {
+ const host = document.getElementById(`${type}ProxyHost`).value.trim();
+ const port = document.getElementById(`${type}ProxyPort`).value.trim();
+ const user = document.getElementById(`${type}ProxyUser`).value.trim();
+ const pass = document.getElementById(`${type}ProxyPass`).value.trim();
+ const resultDiv = document.getElementById(`${type}ProxyTestResult`);
+
+ if (!host || !port) {
+ showToast('代理地址和端口不能为空', 'error');
+ return;
+ }
+
+ resultDiv.style.display = 'block';
+ resultDiv.className = 'proxy-test-result';
+ resultDiv.textContent = '正在测试代理连通性...';
+
+ try {
+ const response = await fetch(`${API_BASE}/proxy/test`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ host, port, username: user, password: pass })
+ });
+ const data = await response.json();
+
+ if (data.success) {
+ resultDiv.classList.add('success');
+ resultDiv.textContent = `测试成功!代理IP: ${data.ip}`;
+ } else {
+ resultDiv.classList.add('error');
+ resultDiv.textContent = `测试失败: ${data.error}`;
+ }
+ } catch (error) {
+ resultDiv.classList.add('error');
+ resultDiv.textContent = `测试请求失败: ${error.message}`;
+ }
+}
+
+// 创建新会话
+async function createSession() {
+ const sessionId = document.getElementById('newSessionId').value.trim();
+ const waitForQR = document.getElementById('waitForQR').checked;
+ const enableProxy = document.getElementById('enableProxy').checked;
+
+ if (!sessionId) {
+ showToast('请输入会话ID', 'error');
+ return;
+ }
+
+ if (sessions.find(s => s.sessionId === sessionId)) {
+ showToast('会话ID已存在,请使用其他ID', 'error');
+ return;
+ }
+
+ const requestBody = {
+ sessionId: sessionId,
+ waitForQR: waitForQR,
+ config: {
+ multiDevice: true,
+ headless: true
+ }
+ };
+
+ // 添加代理配置
+ if (enableProxy) {
+ const host = document.getElementById('createProxyHost').value.trim();
+ const port = document.getElementById('createProxyPort').value.trim();
+ const user = document.getElementById('createProxyUser').value.trim();
+ const pass = document.getElementById('createProxyPass').value.trim();
+
+ if (!host || !port) {
+ showToast('启用了代理,则代理地址和端口不能为空', 'error');
+ return;
+ }
+
+ requestBody.config.useNativeProxy = true;
+
+ requestBody.config.proxyServerCredentials = {
+ protocol: 'http',
+ address: `${host}:${port}`,
+ username: user,
+ password: pass
+ };
+ }
+
+ try {
+ showToast('正在创建会话...', 'info');
+
+ const response = await fetch(`${API_BASE}/sessions`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(requestBody)
+ });
+
+ const data = await response.json();
+
+ if (data.success) {
+ showToast('会话创建成功!', 'success');
+ closeModal('createSessionModal');
+
+ loadSessions();
+
+ // 如果后端返回了二维码,则显示它
+ if (data.qrCode) {
+ showQR(sessionId, data.qrCode);
+ }
+
+ } else {
+ showToast('创建会话失败: ' + (data.error || '未知错误'), 'error');
+ }
+ } catch (error) {
+ console.error('创建会话失败:', error);
+ showToast('创建会话失败: ' + error.message, 'error');
+ }
+}
+
+// 显示二维码
+async function showQR(sessionId) {
+ currentSessionId = sessionId;
+ document.getElementById('qrModal').style.display = 'block';
+
+ // 重置二维码容器
+ const container = document.getElementById('qrCodeContainer');
+ container.innerHTML = `
+
+ `;
+
+ try {
+ const response = await fetch(`${API_BASE}/sessions/${sessionId}/qr`);
+ const data = await response.json();
+ if (data.success && data.data.qrCode) {
+ displayQRCode(data.data.qrCode);
+ } else {
+ container.innerHTML = `
+
+
+
无法获取二维码
+
${data.error || '未知错误'}
+
+ `;
+ }
+ } catch (error) {
+ console.error('获取二维码失败:', error);
+ container.innerHTML = `
+
+ `;
+ }
+}
+
+// 显示二维码图片
+function displayQRCode(qrCodeData) {
+ console.log('qrCodeData', qrCodeData)
+ const container = document.getElementById('qrCodeContainer');
+
+ // 如果是SVG格式的二维码
+ if (qrCodeData.includes('