From b4ab7b8cc941eec1f75eb2c1c5e61db862f4afc0 Mon Sep 17 00:00:00 2001 From: "ouhiakei@gmail.com" Date: Thu, 17 Jul 2025 18:34:10 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MULTI_SESSION_USAGE.md | 800 ++++++++++++++++++++++++++++++ bin/multi-session.js | 132 +++++ bin/oas-type-schemas.json | 2 +- debug_qr.js | 39 ++ package-lock.json | 106 ++-- package.json | 1 + simple_test.js | 42 ++ src/api/Client.ts | 12 + src/api/MultiSessionAPI.ts | 542 ++++++++++++++++++++ src/cli/index.ts | 2 +- src/cli/integrations/chatwoot.ts | 141 +++++- src/cli/multiSessionRoutes.ts | 10 + src/cli/multiSessionServer.ts | 178 +++++++ src/cli/server.ts | 8 +- src/config/multiSessionConfig.ts | 133 +++++ src/controllers/SessionManager.ts | 418 ++++++++++++++++ src/controllers/initializer.ts | 3 +- test_qr.png | Bin 0 -> 7772 bytes test_qr_events.js | 21 + tsconfig.json | 61 +-- web-ui/assets/css/style.css | 597 ++++++++++++++++++++++ web-ui/assets/js/app.js | 686 +++++++++++++++++++++++++ web-ui/index.html | 185 +++++++ 23 files changed, 4013 insertions(+), 106 deletions(-) create mode 100644 MULTI_SESSION_USAGE.md create mode 100644 bin/multi-session.js create mode 100644 debug_qr.js create mode 100644 simple_test.js create mode 100644 src/api/MultiSessionAPI.ts create mode 100644 src/cli/multiSessionRoutes.ts create mode 100644 src/cli/multiSessionServer.ts create mode 100644 src/config/multiSessionConfig.ts create mode 100644 src/controllers/SessionManager.ts create mode 100644 test_qr.png create mode 100644 test_qr_events.js create mode 100644 web-ui/assets/css/style.css create mode 100644 web-ui/assets/js/app.js create mode 100644 web-ui/index.html 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' && ( +
+

扫描二维码登录:

+ QR Code +
+ 用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/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_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/package-lock.json b/package-lock.json index 316984fda9..5c3da76996 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,6 +70,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", @@ -16279,20 +16280,6 @@ "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", @@ -18566,20 +18553,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", @@ -21321,6 +21294,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", @@ -21397,6 +21387,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 +21423,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 +21720,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 +21760,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", diff --git a/package.json b/package.json index 93a4d7d440..f8319fafa1 100644 --- a/package.json +++ b/package.json @@ -163,6 +163,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", 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.ts b/src/api/Client.ts index ac309df54d..b36afdb7bf 100644 --- a/src/api/Client.ts +++ b/src/api/Client.ts @@ -4931,6 +4931,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..332f3346f3 --- /dev/null +++ b/src/api/MultiSessionAPI.ts @@ -0,0 +1,542 @@ +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)); + + // 发送消息 + 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('/sessions/:sessionId/test-proxy', this.testProxy.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 + }); + } + } + + /** + * 发送消息 + */ + 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 { proxy } = req.body; + + if (!proxy) { + // 如果没有提供代理,则直接获取本机IP + try { + const axios = require('axios'); + const response = await axios.get('https://api.ipify.org?format=json'); + res.status(200).json({ success: true, ip: response.data.ip, message: 'No proxy provided. Fetched public IP.' }); + } catch (error) { + res.status(500).json({ success: false, error: 'Failed to fetch public IP.' }); + } + return; + } + + try { + const { SocksProxyAgent } = require('socks-proxy-agent'); + const axios = require('axios'); + + const agent = new SocksProxyAgent(proxy); + const httpClient = axios.create({ httpsAgent: agent, httpAgent: agent }); + + // 使用代理请求MaxMind的IP查询服务 + const response = await httpClient.get('https://api.ipify.org?format=json', { timeout: 10000 }); + + res.status(200).json({ success: true, ip: response.data.ip }); + } catch (error) { + let errorMessage = 'Unknown error'; + if (error.code) { + errorMessage = `Connection error: ${error.code}`; + } else if (error.response) { + errorMessage = `Request failed with status: ${error.response.status}`; + } else { + errorMessage = error.message; + } + log.error('Proxy test error:', errorMessage); + res.status(500).json({ success: false, error: errorMessage }); + } + } + + /** + * 获取路由器 + */ + public getRouter(): Router { + return this.router; + } +} + +export const multiSessionAPI = new MultiSessionAPI(); \ No newline at end of file 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..5b7d84ba02 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" @@ -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/controllers/SessionManager.ts b/src/controllers/SessionManager.ts new file mode 100644 index 0000000000..f0803d736d --- /dev/null +++ b/src/controllers/SessionManager.ts @@ -0,0 +1,418 @@ +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'; +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} existing session(s) to restore: ${sessionFolders.join(', ')}`); + + sessionFolders.forEach(sessionId => { + if (!this.sessions.has(sessionId)) { + log.info(`Restoring session: ${sessionId}`); + // 使用createSession来恢复,但不等待QR码 + // 这将触发底层的puppeteer来从现有文件中加载会话 + this.createSession(sessionId, {}, false).catch(error => { + log.error(`Failed to restore session ${sessionId}:`, error); + }); + } + }); + } + + /** + * 设置全局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, + ...config, + sessionId, + // 强制qrLogSkip为true,以防止在控制台打印QR码 + qrLogSkip: true, + qrTimeout: config.qrTimeout || 120, + sessionDataPath: config.sessionDataPath || `./sessions/${sessionId}`, + port: config.port || this.generateUniquePort(), + }; + + 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); + + // 只有在需要时才等待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) { + return false; + } + + try { + if (session.client && session.status === 'ready') { + await session.client.kill('SESSION_REMOVED'); + } + + // 清理QR码Promise + this.qrPromises.delete(sessionId); + + this.sessions.delete(sessionId); + log.info(`Session ${sessionId} removed successfully`); + return true; + + } catch (error) { + log.error(`Failed to remove session ${sessionId}:`, error); + return false; + } + } + + /** + * 检查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(); + } + } + + /** + * 设置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/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/test_qr.png b/test_qr.png new file mode 100644 index 0000000000000000000000000000000000000000..4e9846a7b70caab307cab7e3f12deeee23d1170a GIT binary patch literal 7772 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 literal 0 HcmV?d00001 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..10b94cc87c --- /dev/null +++ b/web-ui/assets/css/style.css @@ -0,0 +1,597 @@ +/* 全局样式 */ +* { + 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; +} + +/* 模态框样式 */ +.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..1f0f817d9e --- /dev/null +++ b/web-ui/assets/js/app.js @@ -0,0 +1,686 @@ +// 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 ` +
+
+
${session.sessionId}
+
${getStatusText(session.status)}
+
+ +
+
+ 创建时间: + ${createdAt} +
+
+ 最后活动: + ${lastActivity} +
+
+ 状态: + ${getStatusText(session.status)} +
+
+ +
+ ${getSessionActions(session)} +
+
+ `; +} + +// 获取状态文本 +function getStatusText(status) { + const statusMap = { + 'initializing': '初始化中', + 'qr_ready': '待扫码', + 'authenticated': '已认证', + 'ready': '已就绪', + 'failed': '失败', + 'terminated': '已终止' + }; + return statusMap[status] || status; +} + +// 获取会话操作按钮 +function getSessionActions(session) { + let actions = []; + + if (session.status === 'qr_ready') { + actions.push(``); + } + + if (session.status === 'ready') { + 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('proxyConfig').value = ''; + document.getElementById('waitForQR').checked = true; +} + +// 创建新会话 +async function createSession() { + const sessionId = document.getElementById('newSessionId').value.trim(); + const proxyConfig = document.getElementById('proxyConfig').value.trim(); + const waitForQR = document.getElementById('waitForQR').checked; + + if (!sessionId) { + showToast('请输入会话ID', 'error'); + return; + } + + // 检查会话ID是否已存在 + if (sessions.find(s => s.sessionId === sessionId)) { + showToast('会话ID已存在,请使用其他ID', 'error'); + return; + } + + const requestBody = { + sessionId: sessionId, + waitForQR: waitForQR, + config: { + multiDevice: true, + headless: true + } + }; + + // 添加代理配置 + if (proxyConfig) { + requestBody.config.proxyServerCredentials = proxyConfig; + } + + 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'); + + // 创建成功后,不再依赖返回的单个对象,而是直接重新加载整个列表 + // 这是最可靠的UI同步方式 + loadSessions(); + + // 如果后端返回了二维码,则显示它 + if (data.qrCode) { + currentSessionId = sessionId; + displayQRCode(data.qrCode); + document.getElementById('qrModal').style.display = 'block'; + } + + } 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('`; + } +} + +// 刷新二维码 +function refreshQR() { + if (currentSessionId) { + showQR(currentSessionId); + } +} + +// 显示Chatwoot配置模态框 +function showChatwootModal(sessionId) { + currentSessionId = sessionId; + document.getElementById('chatwootModal').style.display = 'block'; + document.getElementById('chatwootUrl').value = ''; + document.getElementById('chatwootToken').value = ''; + document.getElementById('forceUpdateWebhook').checked = true; +} + +// 配置Chatwoot集成 +async function setupChatwoot() { + const chatwootUrl = document.getElementById('chatwootUrl').value.trim(); + const chatwootToken = document.getElementById('chatwootToken').value.trim(); + const forceUpdate = document.getElementById('forceUpdateWebhook').checked; + + if (!chatwootUrl || !chatwootToken) { + showToast('请填写完整的Chatwoot配置信息', 'error'); + return; + } + + if (!currentSessionId) { + showToast('请选择一个会话', 'error'); + return; + } + + const config = { + chatwootUrl: chatwootUrl, + chatwootApiAccessToken: chatwootToken, + forceUpdateCwWebhook: forceUpdate + }; + + try { + showToast('正在配置Chatwoot集成...', 'info'); + + const response = await fetch(`${API_BASE}/sessions/${currentSessionId}/integrations/chatwoot`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(config) + }); + + const data = await response.json(); + + if (data.success) { + showToast('Chatwoot集成配置成功!', 'success'); + closeModal('chatwootModal'); + + if (data.webhookUrl) { + showToast(`Webhook地址: ${data.webhookUrl}`, 'info'); + } + } else { + showToast('配置Chatwoot失败: ' + data.error, 'error'); + } + } catch (error) { + console.error('配置Chatwoot失败:', error); + showToast('配置Chatwoot失败: ' + error.message, 'error'); + } +} + +// 重启会话 +async function restartSession(sessionId) { + if (!confirm(`确定要重启会话 "${sessionId}" 吗?`)) { + return; + } + + try { + showToast('正在重启会话...', 'info'); + + // 先删除会话 + await fetch(`${API_BASE}/sessions/${sessionId}`, { + method: 'DELETE' + }); + + // 等待一秒后重新创建 + setTimeout(async () => { + const response = await fetch(`${API_BASE}/sessions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + sessionId: sessionId, + config: { + multiDevice: true, + headless: true + } + }) + }); + + const data = await response.json(); + + if (data.success) { + showToast('会话重启成功!', 'success'); + } else { + showToast('重启会话失败: ' + data.error, 'error'); + } + + loadSessions(); + }, 1000); + + } catch (error) { + console.error('重启会话失败:', error); + showToast('重启会话失败: ' + error.message, 'error'); + } +} + +// 删除会话 +async function deleteSession(sessionId) { + if (!confirm(`确定要删除会话 "${sessionId}" 吗?此操作不可恢复。`)) { + return; + } + + try { + showToast('正在删除会话...', 'info'); + + const response = await fetch(`${API_BASE}/sessions/${sessionId}`, { + method: 'DELETE' + }); + + const data = await response.json(); + + if (data.success) { + showToast('会话删除成功!', 'success'); + loadSessions(); + } else { + showToast('删除会话失败: ' + data.error, 'error'); + } + } catch (error) { + console.error('删除会话失败:', error); + showToast('删除会话失败: ' + error.message, 'error'); + } +} + +// 刷新会话列表 +function refreshSessions() { + showToast('正在刷新会话列表...', 'info'); + loadSessions(); +} + +// 关闭模态框 +function closeModal(modalId) { + document.getElementById(modalId).style.display = 'none'; + currentSessionId = null; +} + +// 显示消息提示 +function showToast(message, type = 'info') { + const container = document.getElementById('toastContainer'); + const toast = document.createElement('div'); + toast.className = `toast ${type}`; + toast.innerHTML = ` +
+ + ${message} +
+ `; + + container.appendChild(toast); + + // 3秒后自动移除 + setTimeout(() => { + if (toast.parentNode) { + toast.parentNode.removeChild(toast); + } + }, 3000); +} + +// 获取Toast图标 +function getToastIcon(type) { + const icons = { + 'success': 'check-circle', + 'error': 'exclamation-circle', + 'warning': 'exclamation-triangle', + 'info': 'info-circle' + }; + return icons[type] || 'info-circle'; +} + +// 点击模态框外部关闭 +window.onclick = function(event) { + const modals = document.querySelectorAll('.modal'); + modals.forEach(modal => { + if (event.target === modal) { + modal.style.display = 'none'; + } + }); +} + +// 键盘事件处理 +document.addEventListener('keydown', function(event) { + // ESC键关闭模态框 + if (event.key === 'Escape') { + const modals = document.querySelectorAll('.modal'); + modals.forEach(modal => { + if (modal.style.display === 'block') { + modal.style.display = 'none'; + } + }); + } + + // Enter键提交表单 + if (event.key === 'Enter') { + const createModal = document.getElementById('createSessionModal'); + const chatwootModal = document.getElementById('chatwootModal'); + const proxyModal = document.getElementById('proxyModal'); + + if (createModal.style.display === 'block') { + createSession(); + } else if (chatwootModal.style.display === 'block') { + setupChatwoot(); + } else if (proxyModal.style.display === 'block') { + saveProxy(); + } + } +}); + +// 代理配置相关函数 +function showProxyModal(sessionId) { + currentSessionId = sessionId; + const session = sessions.find(s => s.sessionId === sessionId); + + // 重置表单 + document.getElementById('proxyType').value = 'none'; + document.getElementById('proxyHost').value = ''; + document.getElementById('proxyPort').value = ''; + document.getElementById('proxyUser').value = ''; + document.getElementById('proxyPass').value = ''; + document.getElementById('socks5Fields').style.display = 'none'; + document.getElementById('proxyTestResult').style.display = 'none'; + + // 如果已有代理配置,则填充表单 + if (session && session.config && session.config.proxyServerCredentials) { + const proxy = session.config.proxyServerCredentials; + if (proxy.startsWith('socks5://')) { + document.getElementById('proxyType').value = 'socks5'; + const withoutProtocol = proxy.substring('socks5://'.length); + const [auth, hostPort] = withoutProtocol.split('@'); + + if (hostPort) { // 存在认证和主机 + const [user, pass] = auth.split(':'); + const [host, port] = hostPort.split(':'); + document.getElementById('proxyUser').value = user; + document.getElementById('proxyPass').value = pass; + document.getElementById('proxyHost').value = host; + document.getElementById('proxyPort').value = port; + } else { // 只存在主机 + const [host, port] = auth.split(':'); + document.getElementById('proxyHost').value = host; + document.getElementById('proxyPort').value = port; + } + document.getElementById('socks5Fields').style.display = 'block'; + } + } + + document.getElementById('proxyModal').style.display = 'block'; +} + +function toggleProxyFields() { + const type = document.getElementById('proxyType').value; + document.getElementById('socks5Fields').style.display = type === 'socks5' ? 'block' : 'none'; +} + +function getProxyString() { + const type = document.getElementById('proxyType').value; + if (type === 'none') { + return null; + } + + const host = document.getElementById('proxyHost').value.trim(); + const port = document.getElementById('proxyPort').value.trim(); + const user = document.getElementById('proxyUser').value.trim(); + const pass = document.getElementById('proxyPass').value.trim(); + + if (!host || !port) { + showToast('代理主机和端口不能为空', 'error'); + return false; + } + + let proxyString = 'socks5://'; + if (user && pass) { + proxyString += `${user}:${pass}@`; + } + proxyString += `${host}:${port}`; + return proxyString; +} + +async function testProxy() { + const proxyString = getProxyString(); + if (proxyString === false) return; // 校验失败 + + const resultDiv = document.getElementById('proxyTestResult'); + resultDiv.style.display = 'block'; + resultDiv.className = 'proxy-test-result'; + resultDiv.textContent = '正在测试代理连通性...'; + + try { + const response = await fetch(`${API_BASE}/sessions/${currentSessionId}/test-proxy`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ proxy: proxyString }) + }); + 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 saveProxy() { + const proxyString = getProxyString(); + if (proxyString === false && document.getElementById('proxyType').value !== 'none') return; + + showToast('正在保存代理配置并重启会话...', 'info'); + + try { + // 1. 删除现有会话 + await fetch(`${API_BASE}/sessions/${currentSessionId}`, { method: 'DELETE' }); + + // 2. 延迟后用新配置创建会话 + setTimeout(async () => { + const requestBody = { + sessionId: currentSessionId, + waitForQR: false, // 重启后不自动打开二维码 + config: { + multiDevice: true, + headless: true, + proxyServerCredentials: proxyString // 应用新的代理配置 + } + }; + 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('proxyModal'); + loadSessions(); + } else { + showToast('重启会话失败: ' + data.error, 'error'); + } + }, 1500); // 延迟以确保旧会话完全终止 + + } catch (error) { + showToast('保存代理配置失败: ' + error.message, 'error'); + } +} \ No newline at end of file diff --git a/web-ui/index.html b/web-ui/index.html new file mode 100644 index 0000000000..9c9bdf939b --- /dev/null +++ b/web-ui/index.html @@ -0,0 +1,185 @@ + + + + + + Open-WA 多会话管理 + + + + +
+ +
+ +
+ + 服务器状态 +
+
+ + +
+ +
+ + + +
+ + +
+
+

会话列表 (0)

+
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+ + + + \ No newline at end of file From 6952e9d0ff94cdca99a2c9d6a9ec657798589eca Mon Sep 17 00:00:00 2001 From: "ouhiakei@gmail.com" Date: Fri, 18 Jul 2025 16:05:52 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E7=A8=B3=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/server.js | 66 + demo/server.ts | 8 +- package-lock.json | 68 +- package.json | 3 +- screenshot_dadasda_1752774375430.png | Bin 0 -> 116048 bytes screenshot_dadasdad2312312_1752774375431.png | Bin 0 -> 10880 bytes screenshot_dasdasdasda_1752774375431.png | Bin 0 -> 131851 bytes ...enshot_dasdasdasdadasdas_1752774375431.png | Bin 0 -> 10880 bytes src/api/Client.js | 6343 +++++++++++++++++ src/api/Client.ts | 12 + src/api/MultiSessionAPI.ts | 90 +- src/api/model/aliases.js | 3 + src/api/model/button.js | 2 + src/api/model/call.js | 18 + src/api/model/chat.js | 52 + src/api/model/config.js | 111 + src/api/model/contact.js | 2 + src/api/model/errors.js | 160 + src/api/model/events.js | 127 + src/api/model/group-metadata.js | 25 + src/api/model/id.js | 2 + src/api/model/index.js | 128 + src/api/model/label.js | 2 + src/api/model/media.js | 12 + src/api/model/message.js | 40 + src/api/model/product.js | 2 + src/api/model/reactions.js | 2 + src/api/model/sessionInfo.js | 2 + src/build/build-postman.js | 306 + src/config/puppeteer.config.js | 65 + src/controllers/SessionManager.ts | 4 + src/controllers/auth.js | 465 ++ src/controllers/browser.js | 961 +++ src/controllers/browser.ts | 20 +- src/controllers/events.js | 193 + src/controllers/init_patch.js | 94 + src/controllers/initializer.js | 687 ++ src/controllers/launch_checks.js | 189 + src/controllers/patch_manager.js | 320 + src/controllers/popup/index.js | 188 + src/controllers/script_preloader.js | 110 + src/index.js | 36 + src/logging/custom_transport.js | 67 + src/logging/logging.js | 188 + src/structures/Collector.js | 545 ++ src/structures/MessageCollector.js | 167 + src/structures/preProcessors.js | 249 + src/utils/pid_utils.js | 60 + src/utils/tools.js | 595 ++ web-ui/assets/js/app.js | 115 +- web-ui/index.html | 43 +- 51 files changed, 12866 insertions(+), 81 deletions(-) create mode 100644 demo/server.js create mode 100644 screenshot_dadasda_1752774375430.png create mode 100644 screenshot_dadasdad2312312_1752774375431.png create mode 100644 screenshot_dasdasdasda_1752774375431.png create mode 100644 screenshot_dasdasdasdadasdas_1752774375431.png create mode 100644 src/api/Client.js create mode 100644 src/api/model/aliases.js create mode 100644 src/api/model/button.js create mode 100644 src/api/model/call.js create mode 100644 src/api/model/chat.js create mode 100644 src/api/model/config.js create mode 100644 src/api/model/contact.js create mode 100644 src/api/model/errors.js create mode 100644 src/api/model/events.js create mode 100644 src/api/model/group-metadata.js create mode 100644 src/api/model/id.js create mode 100644 src/api/model/index.js create mode 100644 src/api/model/label.js create mode 100644 src/api/model/media.js create mode 100644 src/api/model/message.js create mode 100644 src/api/model/product.js create mode 100644 src/api/model/reactions.js create mode 100644 src/api/model/sessionInfo.js create mode 100644 src/build/build-postman.js create mode 100644 src/config/puppeteer.config.js create mode 100644 src/controllers/auth.js create mode 100644 src/controllers/browser.js create mode 100644 src/controllers/events.js create mode 100644 src/controllers/init_patch.js create mode 100644 src/controllers/initializer.js create mode 100644 src/controllers/launch_checks.js create mode 100644 src/controllers/patch_manager.js create mode 100644 src/controllers/popup/index.js create mode 100644 src/controllers/script_preloader.js create mode 100644 src/index.js create mode 100644 src/logging/custom_transport.js create mode 100644 src/logging/logging.js create mode 100644 src/structures/Collector.js create mode 100644 src/structures/MessageCollector.js create mode 100644 src/structures/preProcessors.js create mode 100644 src/utils/pid_utils.js create mode 100644 src/utils/tools.js 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 5c3da76996..25f90e594a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,7 +85,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" @@ -115,6 +115,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", @@ -2152,9 +2153,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" } @@ -3164,6 +3166,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", @@ -9994,6 +10002,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", @@ -14342,15 +14357,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": { @@ -19748,9 +19768,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", @@ -24629,20 +24650,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" @@ -24683,16 +24706,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 f8319fafa1..ae95022873 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", @@ -178,7 +179,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 0000000000000000000000000000000000000000..ccfa0506533a6a0541dba09aaa99dfd6c30adcb7 GIT binary patch literal 116048 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žPN!R;8{)U| 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

re6$sluqju(2nWb-QtF>4T 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⩔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Ĉ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 Z51YMIEme`*ah3EY^+sO2;PuC^{};i{^X&it literal 0 HcmV?d00001 diff --git a/screenshot_dadasdad2312312_1752774375431.png b/screenshot_dadasdad2312312_1752774375431.png new file mode 100644 index 0000000000000000000000000000000000000000..aa02ba8ec6186ef019c7d435071e123d4e52c00f GIT binary patch literal 10880 zcmeAS@N?(olHy`uVBq!ia0y~yVA;UHz~0Hh1QaQ|myrOZ7>k44ofy`glX=O&puphi z;uumf=goCPMg|3*0|p27KUYk47iO)kPJaeed*SmoW*~JVk%N(eVG)la1B1g9MHU8z z4x<*JaA2DN(1=q4K*L=mofsGzG@LmY7z9#B6_19-XxbRfDWgTfXu&vIO^jBWqpgC` zHqvN|akTwB+CdoYE{%2`M|;Pkz2ni|@o4XOw0At(J09&FkM@p7d&i@_V`}w|59IIX z1Gcb%(>_0=`HLc1d^mym>%bJy49zHWG#o|~!Dwa}EeS?TM6~MQMz`!T1_mxY&{}}e z3S+dk9BnF$HWfyj3ZqSh(Wb&^Qvt21FxosAZ5}|H2O$wbLi}rL5B~uTH+Z`GxvXq literal 0 HcmV?d00001 diff --git a/screenshot_dasdasdasda_1752774375431.png b/screenshot_dasdasdasda_1752774375431.png new file mode 100644 index 0000000000000000000000000000000000000000..b0e070312cdabb76a6d1b05e02162aa126295351 GIT binary patch literal 131851 zcmeFZXH-*L)HcdF9+4voSPJ3NmUa{6*>=;KHC1er~Rwp-o4Tapb| zJ-7b7bnnicGasZzXKNE(1{gi<^Mu+P6Hm3UNALww=veeyFL_;G}p_;0m=58nz6?0OW5iES- z(DFg?RomZ}ALBvP4_==Wb34t!u>;(!r)dEJb8 zpZWnBf%ZE+gufDVvuKeloYAu5k}t!-(LOa4(xwwiiaVpGz_8?67%yg4c|pBv{S8*i zOr~!5X3l#v-MFSKEzJD8ThPT%BE@t<0}zJP!Hq|5v1p5yrsm?>O9i{?Lnn&-#LZQK zM_0ku@%T*yX{IFDGs2~WKCq{z!-aN6ZRVf|spZ{o`FMH>bJD0WRr+Vt_xP?<*D7lf zo!c#++yo4L0Vka8IIxSYZYf@@O80xim+iXB{Iv3{ue&xou1;VMRPcP)SKY^9ZX-e% zbl*X5*vQ93d$rD1mkZ;tcho5=Us2cDTPs-5?;68r>^_PPvxxF!fMjt|Xzy??3E3;; z#nn#JS-e6HV{HZbhELvCJ7`T$F4eyvp`!Bdj-vFgcRA9@bN}`cj>L?QUi~9R9fGcFc}P*WxXg zI@Oii_5m|ukAEt~>_pLW0WX>eQ{#WS)bEvSAz0R3nV*-?lQ#4a4-29p2--h`WuqDu ztZeL$fRQo7XkG>V+S9L-F?L##rQi5EFmckzD(kvR<5L_Q|4tvA>R7W#(Y#13A#5Gm@7vemPkst~l*)(q29sPFy9 zKR7J7#DGZF+xTjvWrOY3Y#8#aE%BVd#7jGl?e*@X4m{^R8@O(lZSMJd+`Zl0a!~g4 zp3zF-9yWY!J0+nfbLel0{ef8(P3NM6J}yCim2}>!Na|2+vZBuFo1wR-df6S6PVTrFw z93BRa$Y@sO=HU45iax&ZHSSH{ua_=p0VnW-Q+fN0P(xKtub;qJcH$N_~#O$f#Z4Y}p%D#-V*fLpkhKmBT zf!N0+YrvJ*+fP>79%=2qS$r407lJ8gT3<0lIDXyvrlL|WAnkZ9zwtNRK$;7EEKbo&g zj5mBysI8(8E@Kd^$CNgDI)|Vb@o>6gElygieoWHm#sU`y$Kx4aRKhUQF8|bl`1wuQFm4l$vgP`G{fWs9_KmQYH?2pa+#OKP7NgWj zklyVwZ{o3-V{L~V3kCNI%5{gVhd2w3NuSdTiY)**I;;qwO_NF1R_5axBC<~Vp$SbVs@U4G*VzEwjF38se^Nd zHm5Xe3`ouc3Yjf8-G)ZTdBiPmgw_Sku)@&xGmD?L)rl#oMaRpy1>GqlcecOwLAg zU>#z6%A`!C2^acctgUZ@DBe-FIf2YDwze@(q2oqe;PPcQ{5rR5Ke=&z4zpnW5tgT) z0o@xc^UwTx@j_b(;8fC6_S=NK*$qTITpn54=tLu^)q`Z}u?9wTJN)Y5;CuhOWQoih zq3+Jp(pLtGljRv-9cO}V05WoRmU!}+gB7FD&c!I0eqfwv!)&I!>pSd;hhKM?N)$GK zY?gH@V5)r1UZ~y|KCh)Rvc6s`*jHHy4bY?v&8yJ1PLzYoOG+a#k-l6D?ML$jk%jlK zL1x&Qyy|WGY)YBAM2u24h5Z8M2y2{0@>)Z7U-+)n@>;+$62?eeMpMdH)CA{h_lq!< zkzP8q+~t$Y$A}7zKnRu2r))S2xhV_T{E(Xtuv&2FbLqRa5{!5XLQMY}AA;lEYw=?0 zL|raqW>ngY#1&_|iM{G&A*ZDTJ+OOlXf3mTlvj;Ny$HC0&2Klzaaoa!E;29QYzcmt z3T2sTP*zf8;1)O9oEM@|^~?}YW_!x!gwo`V?TJtXg}3oj0P#v>u)Mky=W=kA=R_j_ z6=k|Z01m$8YhL@R(Cgg-+pZFnD3BUY035@#UL)+~i(lc~#$SyiN{}nfGA|}$>SfT? zW$QKR1^SuTTq|u905V2DuA_HaT%$>VLt7hlkuuxT?3kLhpDampNwES4gD124cp0X< z%gzqD1}k3Nbz!SB_&7JqL{iSIOw5cyXt=U3oW(yMI0FL+SV^{gXN z-Ag*F(-^RD#V^xHJO#s;9^#y^UVwUb&G2(UdaK;x(*6juhuf%BeJKujPMh0Z3Dn_& zk~QA&om3PCpa@*QNUm|Idr&s%kSLPtsK!;NsA#&B5WM6}&)}6^g|ldmsrPOi__hQZ zyH0K=&KQ+{1grX(j)gNnO)Nn7LZDyV2N%n}c<^&uK1tWMQ{G(o@>n7LVUtfRz(*Xe z<)_&lh*)YfDrk@?+95a|d}vK@0=(iup0!1{Koxb>r7F+b&B(}wOHd*vH=$x*R8dFI zSFZrz5GpD;=WBj5uE7xQ4$-jcLX$8=r5d zqH6vACY1TEDShC8JI_>Mt-|_(PH$hY?}yd{HRro4WdJ=+oOD?L%<0_92%$(S1C+QP zHJ>j5kd4kgDwjlywT6#5l=tKX*HzracEvZsHtxu6_XIY|Kac*UY{sP3obJE(b>S`z zw8?BP&I$|8FP&9@_-IPH095JL=g46?x3Ty5R==b~Op?0Xm|JaDEu9<=F$UeFJ={Iq zhO-J82yoVKR`%oC6+)U6)X%#v6o3M zU4d00=+my=ar%WZ*$YY67{KY$D6`3CcG`f~buoFNyczbl^n#REf-V4J8UIGC-~F`0 z<)UClMiXo~7-hynzY?Px<+_CV3MPC`C$RuZcW=UF!_OrqVJwGY(%KS86Yl%NzBN)$K(<;7 zOL50H7===WVGW1O?yPCFs;AO5q@l512tc?fhHQTi@HD7q`A))H^8iJvA<(kexY}%wb?2rNSoO>M5XQS<=xpLcCP6y zq*KVi27kPRe3pogd%Ah({ku$lK-xsM`d}}KyvM6p3$cLvbC}=!w}LKj>H0Q7Dd+U< zA`f_GJOYTG>rL*H<-5iL%HW$#(~2=WdK>zE2fEw`TjM1YZ`-P14r>vX3HQ69fnI)< zHvkCJALbFaxWOf%+~12+fMgEQW!lD`uWP!%pS;*0{OgJXj*&7^iT@zp61%u z`K5TXuB%DzqvWi`-M-f`Xl2i)1F8~optshfxTt;|5NW)|@vVA-(;#8e;ZfP&8?nDt zB@pE^72?0sc=*r!yBBrV=SqeE(pFh}l2G|AZ7w1AH?w55*!9o#yI3lOUlxQRx_)r- zi~?e(NRH=0cjw7w1SZ%cenH$~kXPdCkZ*SCV@AZNJlcBi19SAxT5DH_T7UdpQr|On z6PX%?-goR$SrL?|a~n>IJ-^-596l7WqNK}}PRPh>L^7MO{3ceW^_ycVhDq191a)q= zRYTVFH0SE}DvQj!`n2B&HtF+kL!UfD0(1uNIinC$ZQ{!uxCzLwlw4i(0!?`4m z+K8q47Y-^Q@W3@Z*>^etkgQT$^msP?Z$QvHKAt6?eCAS;ZC!IU=0|HA`m&nmKhr&U z$3rVciJ|Hvkh~}E$I4wmAZ`M-zwJa>bha1-O%7qLOp0!WL9__MA}EU%Lb!YHmm$ImSM4 z#@8LbbdpNULmN&C2o#^-;JAze!6lByYBNc8yQ_K-uPpOMuL28rm33`P*;Qx+p`t^) zUSqF>VgY~$$5S(JylhOpT=p5o5bX44$hLfI03_SdItUwwil3^qz*LllPB(|$KBwud z0&4QW2j=kh&+hmX0jDZ^M>t4mh2ccy6v@vnKEG zuGiLH8H43^A2LTuTr9rvSQ|0cf{Cf9cxlc*I5-+cLmWZ?^S>lY;+m|0PA^My0Y6`s z{O|og3;bt+|19vI1^%ayKMVY4f&VQFq_?)30JAnDt-N5`B-%-r3$MlM z?TqD}o=ZkrJy!Pbk9CrI809zj{aljv^4D|8v;gCCzr2k3{%jA~SsXyZ6ax=3AxE#6 z^!;f!TkN{rmrY>{JX@RH96Nq)LmbN4nR!8nwm1IseyH2jdmM z45THdWl0?JeFrli?zHJ*9Y(9iG_MRWnu9WP8AOe{jo!3=NkgEr^dQbqn6*{5n4+ea zVAJr$ofgMGkb$(T_3o-&84ysgZg8I7!wTG(`GJTte83LCcTU;)FElVDIG#&z{h?}W z1k}o0Toi`~2M7E6Jww_3{r%b5+0MBiK77b){lF$xR#jD%l|8_Np@tzLA$oe<7HmHD zvB?VSR3X29l9J=Gz_lSTbIhh&+2L$*3tOd||KK^rabCdH=Qm2QKsmjN>*@KZDrSPqh z`d3RBm4JtyTGfsuzK*id#5s4Wi>!hpb7qz#wUi;3E&9U^ufPx%kJ_Z|XI{aHGBzsI z(ZQRCE-(@dABKPjjA3Ebl}QH$j<^*1R9ukJNp2xlX1s=8GCXv@RN(58qg^0P_2Hxu zC1+b@iv<_zRjL?kFclgXm2CP+uR71pm8z>f@!qetI7}Ams@OWTqK2oL#B& znttwUDE-9BI+x{-RAH_Tz+Z~68Mmq(;g#BWpHJ@QyVjWwc?oQ80m2122l`w%b_&Ew zhOyQo1fZpOhPj!ALw;rs+P*D(Z``BQ9}7aOG?FsE1ea%EXd9{oXAafx8ZjI>K3HG- zliYh|3Oz=-bM3=^ElOp>8RJC_;fXo7B~|2a^y1x+poYbVY)cTcV}Rh-!mfc8g3T{R zi!pggIQEX8W9USWlIsLo*TdSH%8C_-bl(G`cau*KHq{=Nirjgu=ssVzl0*#o`BtCv zmuwfm&dZ^UygZO|JBuB;lDNFGUUSX_Olu6M-jG#*Q7W5h)P7-7g@gS_qdybUmMEqv zp(3WS{*nuxTvl4zxedDKD#|WhbM*W*kHSF}1aDY3S1f>(6cyi|-kkapRyRZs z;b^=3g%QEou6nBw27kg=%-U>e+dWr2j%G4>i@n!xwW#S&iutU!N7+J)*%7!1*l2=v zV9M^xsMcig-urSS!)I^rTMs-@nhSg%?GZSYq&bPk#u%8T-hFnLC9gS!!M?Q%n!q+p zCnOv9KiAO%>?U_dYMb znXNjk!}RpDii*lPDVWt-v=a`VcIsdjy~h6j{Hml&QTzo3qp0+>4CK6(O};lUe|>`Q ziY3<{4d2Dk5aap&i#iw8p6wYEXDczYvVJY8r)*H3VYm&%T=*KZZ$M2yfwcg^I>Olg zsh;BcTR*CNrshLDeu-#1R<93zC|Db|t#;nnYnkf?9jw<{qUP0HF0fn%EE95Whp{>& zxi82ApjUE90cA}tCeD5pQ+&Y|o%w2v58S9-kt}kNh>;w~K&Vz(x4I6y!KIggetc|D z57_t3%_kz3q=#~qsC8l78I{*OM=A_*Cgo3x^nLE+#2nFQ`!agq%r$cBZi3q5AqN1>GL zxXDVJkhz-9>Q~HcH9z`_sB_(uClzhWu~Q)OqAUcw|GgZm^*-A`g`&KHi=dQLRT*En z(!`j=i$r|?R*EfVxje%=%%cwf0*q$;9*#OLo(qEDR-=}O8Dg#EHhqG3tNEumUh_TWc5!tTQw;j0q|lbqB0^>rE{(FX z^R1e_!!`r(Be6U_7~LGP`DWuhRZ!L#rlh3NW2{3%Jj`#daQyJsu@m0wvu@Zt2odRP zFyYz^q&?;eubOwsRYa6B6Q`fwO!|OO3tIkz8IC_pEjLa5#5$P7;BpRbJ3ik(e8&(C zid5X5D_ttdgg@*qa&h6+q9GdM zxe-Rhzr{zP@@?&Kq`L5BKA%W*+rQCSMWr z@23H&nO)0U`*cQIvDNxs>0(N;nEaBM1g#2gHR{xg4ERpYA{nq|W!4JXe} zv37q|p|@ICcZ(x$gD4{Q04Tm9q%>Xvv&YXRMeS$RBlf8XxcE5i%7Bvg&QGvv_^=tm zNIYQQ!(^VlhHE`I+T*E}89rLwO+^?R%6BITX)e(5^KE|EnF7idPDNUJZ5TZFVu1?F zo-+)aPL87OII=(5kP53WVa$5+8V+aGoZ%8ZSEGet2hn8gZI>Jo@~pII#5X~F1+|hQ z{z{8p9qkahQ9ph{Nr|w%I~b3=3Jc1)FxmQ(r(No742NyWK$WO1$}_9kjl}FT1Gvs5 zXl$mV_=i*UtvX9Z85kbdYD#L`qibTC5Bm3+c=!`d#C?GM%r?+E>cR+&c?wu)6oZx5 zBz;UzS6QpW!f5DOxIOA4W2=dr&!Boqi$UEz*TmVI8RL!Bm)L8Tjv-smPSkr&U$$O- zwwRXeOCNJIm36*4w!AgoHb0HX3thh(oign!&sqpBO~z9Y^^dc2#%>#=jpvq2!~j&8 z1_azzz5dj4_wWXMxQ#u6Ff~cy&rD-2!P!lFi-D8ucVdj5_|n}CgveCMgT{5#wtgBFfxLb%Td2`sQi4y12aI)7Tzg@I&D~wq5PC^%$_MxL(zIU}g25-O&lj z3!G6)5qcQ)9(u6JY8S=KqTG_l^}9_zis-D@=ubuSiiGaGIyOdfoJUwDJ1>1Z!Sk?c zJn@08W{qXCFO^MO{jD+177={Wb302UM|Od%Csxj9Zz8RBZ|{USvh^}#aU`|~qgjoQ z+6WxgB0mSoP9aG<3%X($YQs{}?N9^{F;z%?>(jU7IZAAuiRP_(Y+>mbi@9m*cyL$s zI^gl!vwn6kT;8!07RW54Lct4d##q`qhycoXeZh0*EhFz_6S!@AypyUOsyVe7ERvfgT3U($_DpbloL@=N{K@KkHRkx|HJ{t6Is9S|bs>MSww%UO7&q zQRhOI=;9)^HNx&F^hEg-t0%9%M&xj=46)jrOs}ZpB0m&p!Q&Fbxh4S{wJxRpMkUifd) z9HrMD7P(vn)@JuQrp{g(10DXXE8X>w7Oi8QNdj-a9}k_8Rx&GB^zJQM%fq#w6v3m{ zxAqH+Snl*p$a|wH^i)2KMN}9??f+zQQw#0Du{4QIe60n1W{Ad}j4;p^23TJhZNuDhos+A|2_{)qiBGbv-{HG}Vf zYE+vXst(wHcA^N(J>^7|FI9iaB2V@LT5|bY-8(bw2SlY37@X5W@uJ3y=s@P$%4#uB zTY$O0X9NZp0S?0xPaj?4=jS)kqLTFUXIkDln;vYoMbHPvMM0iD|my}FZlH+()w1G0JBX)^tO#7SwyoV>9nIC7mWY9lU62t3Xl?tkNr#&ifItTA0SW_2KW_RG zsR-y^bAXrM@s#hcA;I1&V;QO7hx5$PAHNi|@ab7uVSulvx-@12s)CrB6%jNq7jsjC zc&kMtb!@z5U|7?a>7iiXaI_>?rwuH4rYB^HNAB+e5`Zvh)@)7R9wWd1d~D3JBXpr? zDrMsikdLu{Ko6!d5M&gCIQF%V6Y)CoXc|`34Tx*Fg4H4I^&0|d(jUQ7dZ`7VX-G7^ zcK_EywWn;TaAR%e`j{8;$-odmE19AVeRox-0BkT-xu|nEoDLQx!x03#V_Jvpf0SPO z-KTx_&dSa-d=Mo+m!<$^6-^my*5v0H>T-!{e#Ni% zXciYy60#^?=Ty~uSHpK|+(sQQ0I6+9Kn)+kFxn6%F(Zh6Q-BEcZKfyyx|g?sRXzsQ zaRIA8jA+i)k%YA)te&>+NSJZNtJj zZjph&vDlbG9H{mJ){q*DE0k*g>9Qs|1>b(&-drvs5i_NqV7&eFq1>XS)q6ZS9`bbZ zLybCFxj0h8YuMk-E@)Y1ZJydYCS3Nd`jwdv;UCm`~WF7 ze=r+?G)8ZYH<|U!YXr~LFC}3y`*cXvk-?s?-p`aRk!p6^u>(A+vCw%rV#!hb)+Q0s z^Sc(32L%dlT>u`Jn`9rkwE zA>gmfPEqPrzk1(^y+O0%fR~&BY(@Zd7moR3sV{WyyUYeJ=CtTfBraLxR%9=Gp7}^o zjQwqV*W;Y4*zM}iC%909DpUAehiE+JaE@#Y4lCfs`_**3eytmO+W5jfyP7ZV(s~O`EsO5YvfUu01~~rM}g#k zAS^v>VAM>%2s*gTZT>Z286!)3dnW3HxM4RC;rP0UDKKjlaf}D7PD8yN07`ylR2II4 z#tpZp&IXUDBPiSTmZLjk+F4<1$CB+sSc0~Je{Pr>rU$uCg&+=y4Xcm1FHD34GZN=< z>Q1XO15=e#m8pU{hucjE>AQ`9qWx`uirdDC%>3E*eM1iKlK{llb8ch&u5;l<$x(!i zAu~VGYDcEh9iC0RG=B^!K}^k8$A^z+4mN8dl$D))D`p3QKiUVFx-H3f19jjdJCvjL z8zTUEn?)bl4^+UY2BS7LNtO1|yFh{G0dcO`d_Yz~LsM3^e8H&=6yUwtKMGCOh7R!y ztgfn7)nR+7-RT+WDt6p#1BTBAkPRu2pHe5LgMbu<96vt6jn$U3)?aQeSHQ4VXH!m? z!Na%l8?v@`p$8tC4YLa*6Rpg@MiuIfPeN9)T0^&r)YXlFEl%M_yjLpyImkGc@hLr$Ek%gvoLiCcMG|HL2!%Tr%^Z9J;rY4nT^Xr9Mz9tb}yYDsKUTVa$yWbg;7^JQFy0Xi&QATB#fQ>lWU( zj=}0fYQ%RH?f)zw5Y-$r!_`ANmjCa9@ctSHR-O1gaq=nG!txT2N#g%gCZkYg=%DTK-S$7c1O%Gax6G)-=__Gk_4ndk zA#-G^Gb0PH6$Q9}qN1YjQ5Z2u5#089yZONOxA={i!o3d%a;30cW57A6DvYRc4p4qh zd9;{KpiYvn9BiYwJ=$bOx6QPF;hL9+Y&y~mYihC$^X1^ud09EpU{nqeb)#+|1*SD~ zg#a8|b^ikp5Out^{6t7e zEANZBm_FedIcXnIH^BQ`-Q1GJz_*}pbAZ_bZ?iVvrx@nwT5II`yS2@EVgm-}nAUL7 zsTrshgOO;RoWUnepDD*sKvwO7vjY4)UGoMMw3PUb0ltdD9oF4tGLDu$v#AI+#L<&& zIIIa044U`JESFbT6J#?cP^PGG-x~0ozsV*z4XCV8Noc>|V9LLco+`U0PhD}3P>nC| z0A|4%u{~11fMjivXTarXsK0743raQWxIF@R$(XTLxD)s!hcK|PsNt1H{AsDTr+)rA zxhG8T{l%J%dPg)yk5|P%vNP52++q+&O3(IU*BlNC_T@@fex043t{2!|jZ(!FuX0cE zp4czXQ=^o@@wZ_Mi`DtSJ?_6DrPf~%535zZ%b?)UfI{)^H%ZIOgUd0X|+H#ORO94P+Ivrs2e{Q-zKR^RU3 zhoDI~S{8#8>hyhm{K*$-afmZrG(FQX)FL*-N0%!z52i&&1H&7(Lkn2P6I%s7l54~|lxsWigyoNeyQ5nk+G%8b>` zWTCnc)nY&>PFh?0Z|aXBU^eKpdSPL-T7FrzUFqYW{s3xc!WYvTQvP{fP_93Y{`u=A zlOS)?ND1*q>C!1*e-qU0STQ*2=`#=O>3tJ7%>K-*C38zHHaFBFuUJSWqyylcz^)Xz zlT$Y+lYmAy#;l(}u_!FGSm+iFhOt?5%ev(?e76VgKL97??=Ac;tX#i-{ery1-l&%! zJZi+eR9a4#>ll^P8n9Bf@0Egxa#E5%6oKu3-cT5usy9&siZQyzT8s=Zo=N}r@o!@pA|@pC!a*y%k4-hqk_S8Nv>7z*0*9op7{+ zTGP?AurL&UpX_KKv4X`kwmt)J(kQBERSLTP&MjGMrDdGw_XHH6dh*pHwkM{Ts?&Qr zW?1pS83SF5{(O%FWBkbQttp)lU zZ1k-~LZM*F`u1%3a7HJO(A>`E84#M}VD%o(g3JK|*e9qzQ7*9G zU%xOPBr7k&98vJ20e>EcR|>j~*MY3ImKW7;1ZLl@y?zHJ5A4*?3Zqd_snXPt>98Ir z9&w<)BR1s_Hj)TCGThq+*j!WuArI3^89pr<`G~EpFQ&1-A>d$u58apORAowU0*RYW z0{}`I*|NSBPJL+Xs@vlM%tDAqF;m})d(T>&@My1zIqmrO<;QBwBrZgpUcVGZM(^kVsN5*J~qn7_(Yj}_$XeLvc9jrDV zK5v8)i}=1n$_@4ec@fWpv!52g>N!XBS`n z&1irE8g&`Uwp_Eh%#rhZV#MduJ+f=*#KhT7PCsYLzZDe~ojml8bI~V!LJg>u`>7;H ztxtHpDo_Zamav`)_kJ2natv_LB{@WHtK&61%#2st|6sCMnRtQaHSnPo0Lb{;tJSMR zIllcpX2y)}Tcx{z@o=GTL3DJc;|teTLOw$H2a+m`1&y~y!+zIZ7k^&i`CZcY1I(q8 zX>Ud1(Ld!FLEHWDEE?lKCY5Szv!j=uKYRKN0)gb& zyG>2|hr2-{LY|(uXmv^T&)2VFf|9>ooW(I&q0fdtgyVQ!@K_SH2Mxh8wx$QReeodv z!~K;VgFu@Q;V-$I=wR>GHKh5zf)EARs6mX_U!9KDWdvI`9gR|E?Ku-fZ@SLJzps8C zERZfEWKLMLYTY8r=4-(!??Z=#1d{HyMlLl32<-75Sdyi4F8#>jtkH znTctZ{W70*>J%q$+tYh+!>-I)^?s9@X-AgQNVy@JnPaI&Ik@g~`~ziRbxs4~eg|u+ zAJyP9;*51HXHB<$z%EG^70RfpUW(Z#SsQO(uhL&@=;vh~`!ii!@neDyAv1rDin1i0 zT^$z`797seUYPslAa=#m!I=peI0{WL01Cj65bRP4_g-AMb3z=@sf64 z1h?&DeY>R9*Qd*+L>Z!Bh=GfPEC$E*vH0Znt1Z6~J%j1xxKRE6jU*-FgqvQ-v?9%q)`u3ugo}ipICJ^oSao94q zDyac@UFLtHan7k=a>#K0&tM|>2YIZ*1?4^@M zExwsM2%geQ)uUA9&A=#=^DYF`bp`#P4Z4pmwDmM>(5&fuS50{IbFz0j=<&?4z&pvq zr)Mb^JP`%4`?h}H-ElO_`P?$ z?6!z8qCqYWuHc{3rmAh!9PC{gSzL;mT2+b19cTDB)HS~Lc=YPq(;yH0<}Ss|D|65y z;qv6!6&Me3ysZmkVv$SpS^ni04(uC!DW(Iw^?yc`>zc%jt(NQM`Hi9t(Q|fZpo0t5 zO?Fl2_@(u4(4b&LtBAmf4(q?)>9|Y0|6gkX`s%%!Y0ADoOB{%P_PvPPJh4jQ_c7Y` zn!fW8XYzI@OdgI@Gzor+1g7$Ms*KC#(_{EfC%r|-pOg4UKi<#Vz0tobl1JZZ|9xA` z`!mNPh+2mxQ#_TcCh$*P3c*tmsyU$Gt?C209HT6Teyr+HJoVF`9Cw;-#`1A+l>Iwe z%x1D>ADc{1s~5?|kwptlJ*I_Gz^*3VR}uxl0`i?>NA1e@sVrvu3qoKQed<3FUwcz|L=jidcX>FppBpZQ{sQVyv)|Q z1RMmkM`r(Df3JYy0PT*E1mK6iTZznHfLVVxmEZXIyL~F=`@LibX#fAtUS7X1pDA5b z2#YhkcJ2L>5Zm8}L^*8r13S>$c@Hix6F(?fD%Ks3;gd?8u5n4l+1u1vRrLD`0Ee}k zoB+jeJSvL(H)*z(9b;#FdUL{Kv^PpVDa6tbywmEBdY!q(704u=Feo(ra6fXx_2r#Q zH_%6k=wzp*)bqSbwY3WKCwm9Zr89K;N;finIjKDA-{bmzRB-x^S>KsG!hY zu6MNDUjlqqA#+}yWYO&vxFX(B!8nwaI)?v9i%xTBYGXC=Y$&M$PyMV9!Wvsz(6(qtatX%UHhAPsT&1XL%X( zyqoh)M)%|~!3sO0l`lDiwesJzfFKhX^D(;1d1Gv^hW`S6a97v6VsJw#8Q!W5%(R-7 zOSxbkgzHU?f+2DLEP6r2D)PY-j~r2RZ{O%eHUcB@A?rzMGqkQo4z!(X)kJMf;-r-q znfQn#q`mUc&qrW+iH-5D9PPx-n(3`}HzZX=6XUWXBI+YY&uL{xOP`mNj%k4kP!yGQ zQU?~uv^b5Nab!_tmjcU1=NYblC?eHN=~|qSN_A{9q_x(TrG9IgO3xdK4o_op`dyJpaf!Ui z35x;h^kxk$ZPN6>kNq2W!}VOBJZ$;rdVPbkt+w0cRN4fo@a#Khwm&|@sHNZC;Q51@ zmh_y6Aor0vGcWq@hgceqN*g$8@|I%0I@)CBqc;Wkmc9LC`xPE(`+5019++?XnyDEv zjWL;060REE||b-&HCQ7cNG^3nX+fyRAu$XmaGBJWHSy;kG97L_uOM7+xc zHwl%qQN2T@;iwSp%O65O_=cb;W&0;a{yJf;UW_9_U z$wPlgg95H>e1C8y=YUT~m3fQsEj9Cm@u91;tITUYxhsZzJBNnIfgF@Lm&D%&r2dlzZ{+UbG+}Wi71cPXaO&Gqqlmrsa+=eIVn-HzyFYuoH#pV{0m zB{6Br>i0^Y$fozLALEQ3`CdL*H?BhD9Cj-e(PP%aitQ!Z&TRJ^L8lRU0M=faF zd#oD{85W3d5)wKkWfnc;Q0?K>siDH4x+5biv)+Ap_u*Te<+FX!-k||(|M25d$E#-z z3y&8*T=?)f&8|4q{h6z$^QX*Ag|=|UtGxQ6{3KPVgMK_|V>Ru4TG_R%9BDeA=v1x2 zF~9{Mi35r4QPFaIm)_g(ez#CNd5M3n_DioE?}#;VG%OsgBHCO<6!%0L0}(heGHIbD zn>hgBLqxOY*sjxFNB#4fm@g%L+*4qtTk~_*RH5cm zbUpc5%}0a+?!iL?VE_pixr2ZN;yQBke(66_A0DXu+=_UGjkD|p{_(+I9foShLba+O ztv1x?e6#Z_+so}wZ=RP_Qmg|qP1Y$+raf%n)zak;AN!n*KcZ9k_NaUK&MO7i;`Umj zsFSS)pJw zq^2ZQ%z8??C*UMgCxDh&Ed>9#CnKNFclpD80B}I3q71lh zjgJLG{H;*TPXR;6!h_bW#{IwncOb&Df%H`qo9bdSg6n?l4xOm~mNVS7SkG{Zbf`uZ z=lGQw4X`Tnn8r`P%pSHh-buMK7$3?u@Yj}*dBX=>!y$Fxmg2Yqac|CUWS2a(RUub7 zXr#KW^`*Q31lznOm710Rq3fyQDGKE>4i+wU)&Uc8TIb(fz7O}ei00+lZ)7Bv2VF3K zzFY1{L<9G2Mo?I~=LbGDd^U0eAG&urh*T4(7-ypnIV;LeIJJMlSh#fyJFk>+q#kgG zx|+C|G1(GRc>dK*6RFhnMh#>4?-BBuv%mwFqIfA?JoWA$jld&5UC{iGf0JY0V-5O% z^gda&aQT9vva)iT!W?W4b(yNF|1sj%a+iRF-_XNT0>;7xmTC5yDBo_^nq)ZU;QH2l z%xCCkc=JeJB6?Ep3%558a%AqlTnu*I*mNJWew;cWgQM=rUtirQul6NIUQ-X=936Ta z3|cR_0RQ*}Szb`or5Q-Gc^B7&!Qj2c(||K{ zQ@mt0TNbHzuF@4{hS{vtAOG_1`!CV!)7@;!)<=uOMuwX1aAn=EcWGf8*Xpj4^*bPYfg>L~5GE3icI8O8=-M_K{5Pry|L z5?uKYqtgofnKl&9CV%x@yx#t~m3Sa=TcL`*Y{f~~zx&Y1`FZNkN?zqxeR$>2aLJ8T zR%s{i)-`8v<3iw222+Av+}{$w=a)$T zt+jt_Wx0h1I_{1KG)1z(0>Cp|&l#IUj5L*_geS<#@*D8)2Xd{1u;w#5*VHA``ztC3 zofgk}SKc^Jk>%MB3f}kwj);G|+Qt(fDSE+x%l~U#2*m$V%)^V5sF2eFI=yLcyfzdy zY+Bx3p&!R^*|)l|lh=;xbk*7MxDm?K=S=lp6SYH(Z^-``8)01}t1lB70GazS z)_0KC*uLaxEUNT_{q5Y9V<4q-SGpu|4*C1-4N95JQKjQ7YIHNoZ={@@xtogYJjqy( zX$p%-%7cks@ObWg(`k-RmU|3%^!2t>Y?fAkL-xwd8#5rJYuJZx^uH-cpCXmt=jjKi ziA-EvsJMNndA)8jU%)8UHPUpGggK3fC<4;5plIf&&=AThbN?z$Q=Y7j0Y{>Z6W#{OUAy=7RG(bq0M1|=ehN=Ob8($dl}h;(-=-AH#R2m;dG z-Q6A1-3>#hbT^!hzxSN~m-pNMz0Q|2*TV;{nc1_S{j9y#z3vs;4L)Og;}n>oOfz49 z{t@~~NoX7B1(J|zi^&$cQo-#?^zGRUy|Ak)B1E=)Il0T+fr7Zw>79eFY#`5`T0hCM z^l73wJ~I@F(?w|=I`ef!@pSwO8ctfq+>}(MMrDEtJAFQ@d_obiee%s(_BWE-HP4mX z5Gt%|ox9U-;Xsh70~dF5xZQl-^-~i5e3OZ`>|gH$p&hgQ*9l+7`y^1HCRKYIR_y+n zK@}y8M7;U*LY@-t!B~<+O!jN*W=K*mVf78hwx%irJ90C(%^OMD%MpX+W_tx(S%DZW zYu}Keal4jHotWq&dC}OBr6glO+Um_ekE_kd?4DbG$-~FaUqcB=%{;FAy6ns^r^mV`R0=`gdY;GkQ zyB|;ou*Yp}m4Beb$G9!ta;Xi!n>PUEK7T;DB$%PH>Iu2$JJLrqrB(a9m-E$eiIm06 zY?OC9D_KQbdk8RL@FqCXlUUhUj7Rgo8nFe~ZJxYo=PDoi zb&j?@!OXxYPg&;B;lNWg#;ySjJhwW2#H*L^>&`DOMt@gBZnvOmC-3E8zka8VFqI-h zDYs%;=D3{ksM}LZaaDepAxUDuo}b#zUNS4X73{>vZ`%{Psvycqt|40sYFoN!i!KzJ z*_KzRC+uVf5#(?Bt{pq(VT+>$-SBn$hBOjF+h#^+-cfUIUdDl~EHK|_z!(#-JqcXa z=PqYg(|N_8PmP_bIoPLE-ZY^+Yf+aGRrNlKx%tr|wO;tn;7L*gxtC$_^ffDOhLqo@ zOP9pIS}}BGE(4!nT@9UsB)lEJiAFftX;*UZ1q2`A3d+0|l+x5InJ$zZU%?Co*Kag5 zQrVpE-|xbIZi||%0)-$R5OA}{3p&!&Ef*EES241cRF-}#t^m;VPub*Gs_ku67YXyWtOb`KBC%=VW376lTB^G^a-y*3SxH4<{8E==tt z&2iJ7-adZJ4Ud0DiU||0I^!{mW2^mpzu0}uM!hbPidz3H;KpRI1dfMAsdc)(dwr97 z3T-g1vE3iiUR${4QYW~wGZRWmfA80=%ZT&)4n7N5)#{H^5fcso-lqxp*Rp8Hjxzc4 zZoA*3a1$I3mrZ&)+sfmfiDU)pl~Lp`*a?%-Mg{jDioQ^*t|L)r z129DeG~&Iv$dKe`{0~O|2S2ia(XTHq+I}DC2XkuC5}rNd^cTIXoT^BWLc_k6aK1|+ zI&zrn9Mp`9!=Zjw%`yEFme}w@NmHwhp16yCv3K4BE3?B~a=hq*`&(sIfkNxgK(dqb zl@UcUnXfu}GA>&#F~4gq_bs`l&H>n4a6CD=O6Y{oT)%Eo4C~e4vW{OCJKHYq!WCl; zzxHGF>%q>Wn~F}lR}ji3kGiI#$|ODf{G6KLAE>FA&nbqQ4sbhajsx zUFY{Rtgy>NIecha_aASbSJ&(!(Q!gZS#Jx^Qg2*xGZj)TQY=7UU z#|!)ACh{c0zw85(h_I+gZ(nMi6hg~fboEnM*L5}yUDT_jXc+eB_;4!l56*tVEBDj1 zf&NK;^3|8<3+$J-H)2$;h`6N0U>769hoMy~gvU^BR5k{({+6-mL;+#sk{taM;cF2i zTrUg)0yEezaVnh>v1r}AVqle%H!J`pf_=&0?S=~2uK+&@WdC1ICnvUHh!seue{m{| z#SGh!{*TKp*o6{;ji&rtN?fH_pBkF3v@rpnts?g7T6+#d7o`?~7j01Lp;#@ylB=z_ zE~WcL9t9YnTR_2 z6A8ggRaszVWR+5Z7~6iusZsirIe!cwA`R%Sca@ovFqXI0ne(-<*9_h;p%{sik9P6V z-)P7?h+OyL;ci+oH{KvEZ*EbSpopxo+RjAt+j7It2$~L{90RkxOb05+&~(72?hTyT ze826aA3L@w+FgS2GP5zHW@%F?9g&d!z*tr|?$v7Rui+pVe$(540Xwr$6&-}8PloA! zz!~jCkGbv4EV!1WN#On-+KmFWVa4!oe=TSRzaO{Zw{+w8opnI=sv(iNyU9u0L@@1> z2)MGpHa40#>C4w4+a1dqDri9W@8@!!b_bJ-RX5j6G+Zab)4(^~@(s6HM@M!cbmz>F znrvA!7}PvFx3RXeD<|{%D#e@h>&Qp{+hB$}BX8l_pF|q|9E_Y6$X;Fw zi}DJdA=KKH9ZD5x2;dCzJ8CEMW_au09?$N!S+AfbsZq=BBrO#qp84lW=|1)rDB7tS z6K;;mRHazlk-SkrSk0a##dBLm_ehJ^sJm_H^PyoQIbnaI6kPKs>p^(dn1~x49@=Pr zcrtZCYU%b};M6$pZhjIxq+7B?LXy0>uu8*qWv36uHtNf@yGYJ9rsMNjFQKH|OxK5_ zlYTK_CqK9)@W$+bduCUh_9UgOp&&JG6W9vLGgdvCOi^SMWEXwCueo{GdadZdPS?vc z_(*9|A+;<6kFW!6t6{qsU)L{A!8+GPr8`=vB$bL2)u zL+!?CD)Zx8H;y-b@GHGVrCv5Di%qMtnSEROn;?)yCxJ6zKN(U6d=;gVn^s-Mx=Pxe z_jNAYZf59F6a8;7VGeW2zqG|jT&rmK2U-`E;4@Y44^`-V1n#p-EH^?*Yb7G0<8Xlj zlp5?~WPPtrJ~lky)Nug~TQ5>+a=7YgI(aN)^Il!XEgiJVztJ1? zr*Ut|vN8-%y~h1n<9Fs$GJfV9mRKvX(ZulFokwhAfgVgZwrgVZg6**}I9P5wdLtu> z0VG~isxkJa(`_?7`ISD@Qem?N2|?1)_BSo3JQ}k@34>NOT)@X?K^^sbUWEwBW(2XV zBhCe+rjU-F+VtJgMsMv$MjpmDx1GC2h66hDzs&=08H-n*4X3RjEgH%<*h z8_*>{!%n7}-L|~rrBkgdD31k%I#vOk)wZu0=*&>L(Wt?gO=Y;#_Fb5men3@KiS`Ga z&H)X*l4uhw@r*jrYWx>V7%}3N`xlNTVr7H`szc`-j8yA02|g~g5`vXbFNbOl$7+uD zj;Mt+Wp$XWahuO3aI~}IEu=eF*5RF7xGH+4_%M6cQkA~i#Jy>Db}x)d#5j!5$QTjc z4Ez+PGHaW1(Ro$A5X3FBwT=9Xx*q{iGGmdh6o_Z07TUTWb>@OkIbUR?>%08EoU3#c zP!by7N_#Cx$q|VH6&g^NF+Eh$o?Nr4l)N}Rsze*>F#KJbDxz?@)cvaZk1dz_8>OE@ zpWDeuSqinpgz%hYAbjIq|F}1Gx~%ZeUL#rpZ#MS|N3bGfur6*Ygh}^iBrD{8wrh`n zvvhcUBl;4V9$t-olj_BCwPiN6nb6PD#D>v9kx{r{=aIUKni=?on#>DOW^Y~#1In>y zjzNrxd1|L~P>VWrqj9B?hCD2>FEihb039Zr_&y1vWyJ~wS_E5&iNVRW z$<9pAP)h#;plp~B`pw^#v6G6~7m>;rMQ|ae(x(ZttV|4yHsw{7CCv9Y$1&Zyc825S z4qwT`E<5*s_D<0(RA(gw*}p4_^$Kpzf`SmMUsO@GOUcFjO4e(^pMq}T!lbB~0Wy2$ zw^4vt!0N|w-fU~1@=8a@t8${0IL#;aOXZ=+RE~+0#F?*hAh(#!Pl=NQp5Y5ZZs$`u zQx{5GmzOsr@+1+JbgJE~N~EPr5lu$A?Fo$z+lqY25kI$p!)jlsc0f?Z@d)DP_7K3< zDk;!sFE$WrKbr%2eq82Dp|?O~`raJqeN6g5PtU@g7H%xu=40q9Z>%k^7^G;FSnp7_ z-KFw`JD~zKi>T2boF4F>z5rB3=`fSg`Vg$7s_m|6dm?S}Aj0O+RkvS({$j$3j7}b@ z&*S>#_qH(C{I}kaZ><0vYi~6Ad+)aQ<@!io%0LWre7m+ry`vOOBtUDnMzekA9Su$u z##72MuVC|y{i6Gl?nQG5KSZ>Mi*4M)i-;Pq4qk^amcU z99Q~#Dv-Nk>D~S%XD4-OcWq0dH$c-@h~$!SHR8{ga`Op3sSUAPu#cZ3z6>uN>kr$o zK!zeZ)mqiud_540;Llb05x+Y297%24>K+3>5V2t)`7r^DLYs?Z^1@E2$_n6<_qd+( zxL!sIlb)R&xqy8)Q5w+yc4BM1294WV%95Iq5y8*raZ0dTGzBK*B>v%DnCNE4`@bt$ zgXo@Gx#fSPr6%F+{B%)83wE1w>OcHFcY%6vvN3Jf{h-T;>ncVo{@eR(E5ChZgf}p} z0YU@)M0IP7kN}k5?2Gu+_kNu#tR~7?B}xTKOZ%eL;#&sTIr6bU0fj~$QnEt1nVjcy z`%mJVPyMq-pZKm>IjRo#)B+7UL|q+=gF9BFkYL2QD(-sk)K6+OOw#b|5&s}X;HJ{#sM&yD zll(;ZNbL`32iDwk zJ?9ht29Du(dv^F7dipndtyk0TrkLw%zMr(sXx#{cFe}ID)Ibb5P#}w7H{U&pxxSnb z#iB@+OQOH&44>2G>d5P>D;>N#tDwDTV@tDs)n~JsTWIi^SB||ETQ8 zqipIKjY{#tXrEzybHYJ$)X~tH^hy>Ps(MqzBdq?Sb6~~_u*7V8T$+yOFa5hsH&);| zwsK}>jl!fnZc8&D=7OGBkI$JNWtBQ*dBN=3tjhp6PlTptX-M3q;fl;P0BsnqB1J@U zp`*AHVisDBwEvlaw=YdJGzT#xZAcJqt(y{E(C7}9ST|0hW*t*viLyJLQ9;{U`W$v! zjK~^CgRAx>wdyne=nJ_^GPAHs#~0~;ohZ<-Znt>q6}){YUAdWNe@oxk)PdX3G3w-=^y>&R3?RNf7|VZ^;a$>AiCJ}4R*IJN z2h({&T@w=9uV8@*b>w~hY9GuVG6pJUL8{7A;7?>3UKoP->*i0*Dx!}W)$!fh05?;+ zp*O~VWu%AKE6wx*yHo8#y=W~fYU~v0?`dY#4zg*Co*S7P>s391EFuYuOLDsJR%)~3 zz7faDQwX~{?ZscRJKod5@}JIheCI^I^wXpY1?p5g6VPCcEfrc*Ln0v&s6EM|2VL5i z9bx?a{1ni4e*3t>yF zwJ|Jb)~N5v!GZz>p~JgI6S@9+eggE`Ka9BTATsO;_)mS^wfBFSSs1^SR#DzH0sxaC z8&ioTC8U<;&9UhHF z;&f513Kty53V*V}7p@uBk3EyI-(A^L-(k%+2Dx2P*(fj2wI}5wgWR9)UTHx{WVbF# zx6$5$WogtM-r3(D6)7iOK%+ILa08J3=t@Ey$~?{l`bPT3M&`16aFAEj?|i>CoD;D;0N^Q}Jy9naDe!+! z1P}$EKyJMLztk?fng1kt{ZFNbfh@28xm>gVM+fuMm`3x^!V&DY7F?cl@dYAyPah#0 zuC6(~`;e}eT^jw|8!@h1zl?c`28~rrOisYRQ;!ih0#0|Kz>r>(69GrOXn!rNI}Ho@ zAQc~{1yIe_=H@pLuKoBHAMJ%t+8^E2S~%OzUdx{|20sUbYx`J@VLfl zhAR*;uM`it2{6wWh_W#>WCGD430V)Xyu7rue*Ltthi@k{ie!w$3rN24CFQWJNobAZPMBhJ1$J8l$qQ+^eu2^M9*&gQK6BDIWRRIJ3AE?=dd{cu;1n~9j zPzNpv4FU~*b2mMGCtJKzV)TBNZ4ZNjD#&`^U-P8Tb#K?#t=0f=4tMV$Qtbo?sSofs{Q!i_-a(_=s z7YwSF?ASI8VpHq;*z@fsr zrdW0V=N+stvXF<_00|-u>7FuG)T{cJ0(+-s$|^=8K;z%|RI-c=t7#!3UfO_``W*kP8cj7;(Rr%2(3J|9>)SABOrLOV9nkHW=&;G(t2e z@uTf+WYbaiqy>z+NHF8sGq=C$d5zWuSNpu575iEQ$yll?pR5vMd%FEcW`0w(n<3?N%1N zFcx~148-sHTjui1O%^_Xij=W7x>NpGG{*;gquUxsJhHIUorw%Zi69GC6g?DQkuq!= z%EO$mo>y!ZGplVe))^XVMdrKH$JlxCH#k=4jl{bRD=m!)&sAyC}0*D-p-r-j>K?{~bfyL}%CeNEz2C7(=#7vVihchhj0V z#k+;e1+?rhORKBlO;x+3UJhg5=ov70oDUhvSSs2~xJ|}}CMV^%#>U1@PO?*E$w`@i zgOSX3;mLPfX-HXvF-gJE<=Wkku_-8O)aTu&-h*=|TCaZx{Pvq1)csQZZ>@+iT8j9B z!>lP5{{%mBjSvX=z;-^YD3|B0FAu zH0&LKbFx`5GcwEP)C-nn(#EnBd=ZmOs*^tCQ!dOY+lV+DTVtY#bzqa{vaz$NwqGd~ zjo}Dz)z)6gFUV7gjsw@0p~)+ladl1YNA~XQmOt4A;IJl;cU@Jq^(fWNelH;A<2bxu zK}zaesNtX9zpzb-g1Fhe7CgoOV6%|&j+VoQ+h)gW`R0fP4-+#C8S1}2kSSy_@y$Mg z=aNnEJGFu2kkNr;v#0qjB3R4K5obQK249PgiT6vLvF?%y>{d5pb?8JWPy_@7n|(tT zYOyuO2E-S6OKZE+$sd+wU$a&4Yo307j+_0y{@^?&WPg6t`f=6HT!Zdi zd>kmI41ACOvexDB2d38YTzjujU{b!Y4kl)(+t4hDSFoJWi|QHp6#=bF??p?M z^EK31ZFi>WZ_L^jX6=6peVVUzjdJ_j(w)X55f`rP?gCr(6#9N&WPkkAmL<gQnZpk@QAgGau*EO|s6g+kg zPL7++|7hkW(uX}Wc9ws+d3L(ALXLpIn=wiZCFSLHU#}NzNA*8$Klx6Arpf7^E8}^U zp)fyIxAa#pcl%r9W_#YGfh)c1B4i>lX5+sgn->{5&FYXK#p7d4{>Y zsMa(x<7T>`PX&F032^bXqAJ{;nPbGSzUZR?nc#jx7{ z(>ioeb%^5Y6$~~X>Qi>V*$SQgyP%}hEw?dNT35vLeNL;E)#Sx2IfFrX!zsNJ{XaV` ztp=W@TwI-D%zlK)i6wQC{>o=GHU`!g)Ug7}FIPUluzrvF*Xj6{Uv?AUmEj_+c#XtO_^b zH*9#>vQWdiVvez;;@OqtFO{~F3)Mi280Eu<%v*7qcjtg}(bhli_-CI9&uN_V+v=1DXkn1!ZS<;>UgN{$ea}x%p`byH zdeiL=+rj}!ui(N2PN3LmuT`Cz{00bS&~TGu58IZ1Os0GV)io(ZPEM}_SLNp=G?$t~ zbP{H&(%1w^z3T6A1d(9-pxbM?nMQ~`zs&1~a8N8KA})FtFI~OyG^|@(M8aetGa}{t z_W*E5VyNH}L$!Y5>MF-+0PD*B>PhJr+Pj9j-j{A~63?zjk~E0NVv0OM3~mJi#8nEI z_RaPc=NVPi69K=}#v(#c8fCXy%zt}rczAh&OM&DJt{nMV-L<#0jT3V*ate{#@-$*A zhFT4-aYfp3>U2q5)&ssw8xwR|9Oh#OrCL`9yc7oX%B4ozfq0Duxhg6VvttOoiVjX= zo1b9}p)$_pMc zpx2a4_?S4j5;2SUFz4(e0B;Slggtvw7aHjf>lrz#RQ+;nm<8X;D_6L!6uKgzK!biH z?ktqF16V*JF>`RP==5tH=t*yM!mc$dQR>T`c4{Z~m>-XQy+7x)zaG%27q8l|ls<^$ zounF|K8t@JBL11%;dS)8*Xygu)?Vz7`6UCTH+MFJ6KFT*^6tY71{HdW=8KJXetmTU zxrUBq57UzV3B_ok_XWI5@0>{L5Ar~~TXLEkV7Y*h^X%4LByz0p{e|%=fZS)I`T!RS zP#W-r2Az;wvPf3P=Dm5yN`^TT6s z@RB&JzVrsdsV8n@A`|CRKy3y^;8>9w1(d<4?ZVdvX{BJ+9z3#o%l@9Z`uZCG$OJCu zM8aODEwBku{YQ)?CB(Klbx9krZ%Th2zhyI)HrG+ipXF;>-Z2VOSZ6zO zR9X2P@X>v%6f0?P@gDlgS!u%L=yHj=#N~K)vpBqRGVy~cQ=F#mo3LT@ zClZ$TGjbu_x`Fr{hO0yTpqywf9QP4Ak=eb8r_?BUxOk;w=bq{J=BcS)bT!7UnEgHVr*lD`AgL@K7_ypY!~Rgnn#nVv_`R9>5{+%e zrI#UoEzU3SS%)UIs*ISRx>Lz_)&`P!i`@U*-(29)x{(1Dxk0aaXMM~H>+%;m7mZWI zzPxK)jj=9##Rz;2{@bf?wE}T+PUoeOht?*TZlr9WA+7}eL{aZYV(tN1qk>?--f`>37U z1;5{|*-3X(gab-keA<+S(y6|UY;Z}WW^*a5xR zgIP*nr_XFDaCT=Tnm8Y!BSYoLf1iS{eO3UjA{rq}p;{Ba&08CL>jvjrSzl27yR-Ri zd*l+UKXxPx$N7Y4BuD99=)06(l<8bHzmX9L`d%%d;jlT@dJ1U>zU8ts+y{HRce7dz zOg-7nRX(!BH!y(AYie5c6)dtZsci8)W_tPu%BcRZ(KlcTN+mvMLQEt{nQ4+|4eWsa^96GAeBT#4mXwQJp8syjE~YkOiE|F-4$y-4>g{ z@Q7DiI#Ayl*Dp+$;omM(FTq}kywz-ZM>42c?Xa_oyT+w&qT_nAsL6)CVNpZvg%KJb zTDw0a1N)T_fW|%eZFbKUugWwnDd7QU)s)>UHw3zMZ5-aQ7iXz0@Q3X4UTD^=yR&_n z%{+y3*xyWGKWIY_D#Qn*1`;g6{tTD-vXd@5DTK?R4?7IQO-kGJH2v{G{mU;9z+dl`*>lwHE|aO=fvm_>zM52;tqjOzU)GO$Wcfna9i*tX#y9rvit-6p%tJRfIS0pDH(fs-Id}Q{BRf;1oE)Bv9WVqN`Jr- z6UoLMk#bWYUHiCA`e7d!O?(1GH!&OLr@c*;@m7o^J9IAkBTg(C__Z~$z!5X*@ zv9TbGNi-bqE~um=q@{PEh0{@XoyX*xaE+1cEj+!lK8wu0et&-8EV9`s%<@hm12I}25y<_hp+iw00kUu~U- zR}zow)gW&tz$#OgE9-qRX=!O{(-KcEsy^tx1~=*PkZgYG`>GRftfQR@ zzPpRiMM)(vA~=UN7yXAe9h=RJTbJzr6zV;+Tv&iF`Ly~x zb7TM4*Z`0u_fm=PwJd2ckf|zYr4dk`K3*HdX&yue{S5xSjGz5C6mZ{vu{!K+1b#@7$Um_gN&BaAk_Z8xz}Q6^EbbQjTw4*c`2)^)>c>d zC334RhSxm2*xBuDZ58C@PYw?m7#J>J!9Q&dDu-B-SFUQ@&Uj-tsmEBJ8DFzB)yg45 zy)cY7KM;QkXg%AV-Ct-*3JHPI)6>^y1_T6v9SmMzyoQF``#ewNTao!B!b#4zq=}LB zr*|g~^{^(bNlLG`k>0F4o)|E+r{od*0nK2XveMEYo}TVFXqcb@PD)D3*49>?-R{+8 z-8q3St8Hb$uLx`!wTga6A_$jnAOkbu&5^`|C$>-~9agxp;W71pMzGS*t77bBT|?4(!e2@_Pn?oGEW(xl z5;D|y69$9rEi@6~64e@se|jCX@y-w7UT(tSr56Sa z`4$d@0WBdTL+|rv?xw7(iwi>#g1T%;Cpv0!4suW4F_%V$xLnHa?AbPSTT!c_Sku6?klub`x`^!X^p|92%k-3Sy8i)*q{$Jmxesv>D$1H16y4 zHZCT@jKTOR3i6}o{!x>|o(jkT3HcA^rw}QNBtVrsYTnYJTj>lW|4GEiRCo4Q>e--{ z9cs`ooB7I^r@OY!SbBQC^jeQR+@c0}UCbyMN)GjPIv#oC_?ApYpxQR%Y)0e%8HO8) zK1q)@YVs{mOtVQr_V4oO|^IGzaP_F3fjvc^sva zH6UJ$Gg?@1)jwEr4Zhp?%pJLygo72jTF@4@5jT0 z%JYctlWuvs*QdH7fv8sR=aG?-n1}6`P<{SCTh`c*h%fuqARjH)<72^LO7q?|MnyRs z;OS;f_o3FVjaHNqLWG5dA$;hb=7khNkDI~%G)H=>I#^y>mLR~QQZlflDV$Bc(EMdV z(|eWp%4jD#q_x}9P}yPp7!5V+aZA@`)@pLq*P!DlI3&3~wVqIlmYUjmD*6x-j88PK zuA6>B;#={s2@&uk0mJaq*jrL!Hz3)Els)X3LFMmX;VmOW?QEeF>bC zz`{^ciUHhxJuiwjNB&*X(TNnT6OJe^{X{l8f zL0z5Q1nR+Q>aZbMMg|C%=>A=wfIIW*>*JnptJxO)qVRk6OxNLHe4A~u@ERPql3>*U zf!I}A>+0$XSNQoIy`zvq$756uznk1_SQ((QpzZt3Puq;)!h&4UyuSRMuIMaM;?+ZN9yd{N znJyOH%R|y<5Dr7baz%yP8VV(YcAAMAF>?rU=a<)I%7pVgc~P`?_j68lGruoFUuTK4 z3Hp+czD7#yMyDybp?f|bxt2m*(5PTrI~^zSZ#6Ms*&iBVB{BOkV&66&r zdvLBo64y%X9Z&1z3S+?MCmz;Og+Sw%EeiBREiW@|&`G|v=;kGv%u*U$^T!U&$p!1D zuj?{peQ8CJ<@mi*V{?r5#6dsJIn0ey62K`P#ycIdk{)^=!|(3c_CyO!I!Dzp!O zks!LJyHhz#ag}@OWiBW&A4Et7YpL{00QXKSJyllsCwr+C!Uxoe75#nPImt%fLak+k zc~))1PAVR2+sPbR6=3bde2PM~Hke{aPkIa(@+^ZLn&>wwCzm(2}MyWY29+Ygm zRV_3P4sEBGfsX&*J8rvk*!E>#5O5zZ$~fpNR+fg!-ZwTV(W~XpiWuB4rSU@*hS%o= zdspL_Yi!m@ugD6m$WL5r?C_|lBCUqyHX|j* zi|MMWrIDY<&Nw!&?s}dZj;P3q>dn<9Ko-*(Z5>7lld9XJV$KnAcLxq@C+6qVMWT?j zo+8D+1X}E5zxm;TA08g+E?mAXnKtD};L+*$eY!n~+7$TxJHNX6oEHiSnKzV?$3=a2 z(Qv3B@4bKj!$0NPXybV+SsML_=p-RHyehc3gJOlWYULPKIdON7te$st>@(HLTTwsa z+cEj{o{Vff&%pk>_r9#0TBRXWy70#Jqw8fZ1tfpAe7e}@yW1okuZw1dWrKrYvKKj> zAlCF+WN)yrfM0QP7$yuU#^GjUqo@CHaR)5&+1uUHQtOcg z14uLI5t-wCtr3Xj4rlzxxV}}L!}M+s$=JlGaq!}ULz}XdXyBhN14dViOC-4a0qKdR z3rt&Ae0*%Q_yCz5@d+fd$hO+4KkhB$dC)I#*>Kv=xEPKr?;)_+L!x=~MmArzD$ZDd z$k_{Le13$aLzF?!JzfXQHv+cg%sVoMQ)%JlHrEQhj~4Cth#?0`2gm3SSM?^xm`!r9 zQpd01t#@%T%CRpXRH4rxLn5`_PXz}>Wsg55T{D(55Py0wD|*1JcvJK6xJ{SpRU{BF z8xAG&dxBoapHPs4@YzYOTv2&kV~x$!`m_l+tChly0_x5Po_g3)N3j!q3keT*!EuP| z*ROMQY7moHk(#NoOOxxxqMayI?pcWJTnRy=_=x`k(ig+x3Mjjzdjf!q;$lYg{zA|p zT@LSXe{$MMe}HBsxqD0p^K-_tPE2%Pcx~uvB$bAaq~o$e?ae`_T)(?m=~bNHf2nay zT4^q9F4}UUM7k+7&QNo(j)+ZL%2jU2@G4CgiuLOFSlTCcyZI*voKhaSfrAQ3&~=uV zeV#L-yg+;60fMq29tTTlougUeepheXPybGP)=Dz?uUQV;b`gjD>e`ffl?mbda^Px4 zE3u$tx^}c4bQ%N_fPP}_Q>ITLT+Wyi>MAw$^*=s&3Zd_m z#4@L3Z-(o#}g{k%NqZuA^a#OIqzF4HXH}<{5fQuUPDAdv-V43Vxc59w?;2Rnlc$#da*(;7UbJa zxrvFwn>%WMf3bLWccbwU!Lf9H$VbQZxo)P7Y#)H4^gSiauxQR2))rD45W7hPv;{3= zKi+sHIHQZdwbVN?e78wuzy+IKX9vK?pr9&b0#6!>S_~)cW z*1UI5S?uBA3fOgF9cq6Kjbz`rSX_dBmIdnCLWH@w<&QnqdwOaUY&081{rns`nzQ%e z$zLFwlXXg(ZagZVl~q(OKQj7}-i6%O62F3w2UL;1GX+ndadU!YPX(rE#~KstxIA>n zS}+d!cR=|3=*XaS{BFd-p{cztw>D9ck)e^1An#1U^|-DeFAXjzbsjia@k1&py(u%E z%*Y5><@(?Wk=N6YH?(KDdm;877~BTQ6LM!?B8`c-d7xJ4sQ%Xnwwq{ zbt~0;DXfT&j@~b@m^K8h#lR}XS{%c(Csz+64F zl^vJwds1iri8~;H!GM3&lQ=?=mK~oC@=NSjSV(jFq($y|eRR9i!RFz-z>g;>1T37t z30QuJyFN!dI6gT8M`E(q+uPeoN{Xs-+>4KCk#MjMINqlOBYj;>N!X1SJl>FmE&&}GvJ!P7cNdDt41Wi5!x*Bc?31CJ~=&ISy}1t?{`1q zu(Y&9L_|y-bBP3W?uE9`Xh-T`Qcx0y82OJvH5C>!+oaxMuxSpmpk%q7@^|0Qb338j z&ueXmh5&YZ;m(&0m+>ck84?zj_P9mAKQ@uuQn$LjZTv-CVRa&C$TxQO9N=gAI_HV0 z%c!EzT#IOiQR5?w(eEZ~A8EDfrA2a#MN&#+v>?j6i`n_YPwLrh0oD(V1tMx5? zdqKcy#A0-Vp2q*k5(Nb^B&neh-qZ2hr@x`F-81$0D$673-zaT zED=tPuS3_%Irj0~D*YovWa_|aadawQwq0B4nW*b3>t>5o=K&sKNy0lP@>*kxvG@G3 zkBG253id;QV?cisMG7 zjLFsqm~nsaF_hjUW9Xp;EJEEmQSSNfNBjZsi`OTN`Glwv2_N@{O2X9G3h$Rj+UB{P zKjy1A-j(8&B`2r;K!c3P%0j+5EbO&jKMivUbwxH%mMFAD`zk1ikU&@St|z+d#v4j070(WT{X^Po$Q}EH?_{*P@~>Ge%PS+X=TyFdy@&S@ zkC{Q8aDVVQa?{0pscD-y&T6(2u-&&+oTCH`g z(Ao?}m)0JV&h@H}aLgYA9X&toyVYwr`(?-==EldY1&$UQ%cRa~`9C@AeGZ~HF%Fo- zjF;OHuOK>iPhkk7$u_3vYCrw4(- z#hG)+!dOPch$47tWkXO()4F~z&vwZ|lQRrXcvgiWVXhi>6x8O6qx7{77eXD6+bQByc9fXY{L$HCr&U+W6r)M(tllf*4v3cvH+or|HA6=s zZ|}0#6=NZrJCst}d7a))O?CPG@bdp8aOJ>>v@qvrc(`9lSWFBoZnrC2v*a^4?UrSR zG*|p{*W>5ere0R0rG5XZsLMQBe*D9-KVPrh-!KWzUsVm?YkD2h&SleS@VM zrwDzs{_@=1@xNC={Ebk?0Zw8%MdzLDQowcM z*oN0ujRGz;PCYU5P_YR3v3re}_maJ9-7IhLUwOD6cYM@nH<;Q~ExQ{wRYP%GKvMFH z>%n>v=xgCE+DwbBH?lWe58oLYcwU~r0lH&ucBB+Pe)Hz7uQxLg&mH>xYPrh3BtDOr zkIo`03oOTVADQ?O-E^lyc|d>l_YS~MZGKNHSF%!lk3FYVs`avb%drk(8B0n>_F2ei z`6&D;CH~;paDNkhhbT1X&5dulRB6~B8YR#Um1~$Q2{9U!%0@xd(Eg2$^O&EB`n`&C z(z6yQL-vX3P0tm^fnnnXl}xG^l$k=Ti(u{t|DJ$>SCHdfW3>Kk=oZ7GYhFkTc?GICkN|h8Riu+DP70_c&GqZQ;{WR+w+}#9H zzLYSlQVwaQ5_*mmg=N6o7bfbyk*s<2RTnZ77WF`fG$D`OYUZ>+H$IW_O|c-tadGq2 zVc8#oPFDPCr)PkiZuAetu|`lKjb;22pzp#&6xyt$P~2TAQlTg=ehFk+F#A^!tEgK_p5n{a9vs$+Dj^ebRUExlQj(IgK*hkYLGf^R#n0C)+SfEu zgKJtAsboIpV;5?%mw8OV5qWbbwX!_1FtiQvtM5!Dcb+ZU72eRdT^OGum2=fI69}yWF-B^Wj~k{Wo)7T?Deq;K8Rs=8HN26NlOvf46!>9yrz`ei-hDtp zpM0TdsGO12Z}rl_>vQT_LrhMeyAoCW|uom}sW>l<;Dapr~R zvt;?>*wMpO#$jtRAu-Wo_P0N0DV$2I)e=OgT%dI^1{}(T-B8OC7 z71_HYwyo>+d#6Og?~bNb%Dm9!LAU7Lc&%RT!X}(KlVja(WV@hUbA~B^2svV5wN+JB{{Et`UcG;Iu(!K=dbAbXLt@fuC|y2UZ+hmZ zD`=WCUFi8j>!`ktLO<4YF|oG!_$f0l#eK`TGL1GmX9g zu>Se;$9CzCx}n2re^wwq8=&vFPyCtlKXgz1F5edIUOGMw4cCzB!J;h>G0J8f^YE*w zd-H>rFZYYgTVFDt<*Qqe#^yelqRFPd(D{j=3^O)0UEAEu|MpF18+*ismw|zSm9;E0 zGt>R-@Zir=Z<)96dUCOZK&tI??*Sp8p;{h%jgRGpOvxxIDcxuV;Ca7ae2$MYzNAbc!M~bO!rGyo zz5d~DKG@&?{`KCeFl38=p1mh<^-O{W$fN6@U=*Y$oYyWK4HgN<9m7GYDn-V9->1eUeYy*LkVyaee)UqavMp zGX3vLBzr#cZrDCJSO{Kob8~}+>7%eWVtdN->R@$%mYI3~=;%J_zU#jWo?NdDW0Fpe zGPrsR&BiNam6fAhmS<=6%+2Zb#^6tYIELVa-Lgp*rQ(;p^TY@Fjci0FtvU4$NcQ`r+9-$q<7#Z*%6hHaNC)EbFPej>+SLO%<1VV^o0-)61W^M`v_lW z?#$S*YcTjKw^77Z`1$wzHe$B6zHNQ`Jeq%bi`nhiYV{b-pGjdGudpWDWb^Buy2%IP zx3}iz2{AEQ-@bh-D5y6ZdFk%%jv)D(TwY$Dlamu0J38{?-IX2NeG_sgZr17#9lFZ2 z`SaGaw3P%7D1PE1Vq(3oB-XL);Zmy`nV876G&D3kEh{cB*RFLruv~j2u#9z$*y53r zlENN=H~{qXV|D|J>xWd3rluwwRqF2M1}crq*YGCHG?n9@cYBGI=4^&FFyj~~Bp)KRmi~JK_HmmHYy%X(=D*Dc%&w z(_&VCXw1J?>+!}p#)n)}pxDCk6&i~Q@cbD;dwY9VM%woTJReway~leGyZW7h0SYSW z)pI=ZM~@zXETFHi&v9e)Agugw4@+if^TGWm2Vp}e9gncxJ@Ae?8#~^uxC$5lB*DYunw?rJFPk64_n_B_le9+{$a2281Eb@Xjghis2! z*MiOJBa8>IHc^dPra5>cTox+yhkUE-W&#NAVB1`)*3LwYK)Nt}Cgk zKEZXXf9L33HED4&dH}6O>kH>6XBc>t^z>cYFU>lgvgbE37y}*5vleY^01IN`ermHc zi7bV=CL&$7HKN_AS2|=wwJM)KU)-^FGFnXA{`A<9* zXpD^~Q%-z_G~G<^xrP!TO>Fyw&%rJb+eag&iDtxhy0FIW;S=L0w*=voRiz+O{KrJe zz|4%KoS2W5^canmr7EF{ii*m*6nh+ehE5Psc+~y1iZ|d2xy8Jip!{|a6|vnUhw11e zoA}7fx{~#=Bd6UC7YnVrpGJwX*`M9QDCdu*bh>$?QX9(M#l>L!nw64eYME0Vy?29* zqVXG=kZoy6#W&D3U0Z>%f2aU2qVrr|)c^Moc~TRdlG>>&Lgo1oo8|Mj4*3z?-AWbj7=P_7V$QiP{;Wb_|^x(k*f-;>u zPsE^1ok(dSp>&uXmZf=y{d1;p#U33El+aVV?n7=-p-P>4@I5PCH4BP7>L+Xa$4VYQ zV`e7moUxfD;zJ*?tsZGmLg?6r-P5_xyF8k6PD|Kh+~C1k;7F=J(NwwRrnxxBt^W@_qsx^FCN zadh4!{@~Y+YgDXSZAjsd1Tu1x8mki;j_uuA-FIBV;h5)?vj=#8U$Cy8@t#!jCs#S( zgk4@d?ut`?8-~Le1~BanM40L}%}Ez<T>xCUZ7-jC`j1uiEefKBMd`(M)d0?oq_c7CVZ zyZ)iA!=1hlm7bQUHzzUCa*Hk(eq1SHq2vXSt$^%pB9OEz#P^&wQ^tN(vg&z0HN66E-?mR{h>;}Mx3J~@Rb?VoqpCln z7U#zB#H-W%uE8Vq9#MG_GsY*v!O~$9Bg}{nA8COi}gh z12OkauR)V+a)RETK|N1RcgMx^rp)N!-Or!f#S%f4G4XkTfOY9^036{UTo*e=r-F_E zFr{K)gJFB>PycVH)Bfy2D*$Q%VT~skELF4=oJ^wKV3T2KYHA`J%pC{2RszFFp6$E0 zsdc(D_nGkm)m_2TK2c?l4H5a_!?oTw!ybtm48j@$X?!>Js{1}|NGZD8);c+tdXur3 zbj?_O4p5bhYropYnitK~*>}>fXR-%$HYlDQ1W!{%^6!EP$7x-rE-mWM0|PH9k&uwe z$`r4Xg@XuI=I8gra3sXUOa`(|Zv8&qnOiFwL?+!A*13jOa2U0=shSpBW~@G85$9)S z9?!Quw6@-Xn&S|vJ1#DD@YcB)prCyG{rk1-4FNGYyD|Qwctk`v!N!oBquJER&o*7c zCwnKPuzL~D-woP*%IyTE=4;Ojcb6xj@|TWVgcn<#bqVSPE-t7OSdt!lWgtv4k0Wrw zwIjpk)i>N6b=|hsS2#WU!dr{ZmU~imwG|L0YRbw9G<0;wLSp@TWKqawR5UQ;sIVJX4vaNGEH7eEYF!+XxF>C9+3(u4pLlij zRUEr>m2ZqYJ}2MTRaK4Q9|PTi!v@d21LnsF3CUZXb_UC%&Tmyr*St?QGck= z-SU2QNM2G{xP5?KR;6BX`yr)FkB+V`sgO4s%hUgm%Zck;+7nQuJbdr~T11d|AR$bL z@(*@)M5UxypJ&c+rT>@wfB3roz+uT9i2Dh~c;nIi>XGalQ|IBP>MY}A-QafG>`*HYY6i-$ z*nFY$Ok7lKI}5b$;d~%DHNsKl`mEz2$#bAH=Xa)+eon{>W3`EhGu^$@tlrgYHg$QN z>^_fw;%6NOZf{q+AsmqbWCTPW_~)F0>xd|Ghr_0F{0yrR*kq$VwT<(IoKHMe#KX17 z2&ae$mca}^fB%bZcdfRlco{pepEUGFfQkZehntMYsai-tT(Adq&vc>c&fM5qntA+C zQBe(pNdJM`)%z{_D{jiZtx-jU)BbT=w5Sp}h1|OdDUipvUnTCKp6C?_lH9~87XUTLB<7DgqF z_+~y<#-haf{5iW#8m7MlL$zxn`vzx&-j@-bJ{Nm}8=ELl9OZnMdI0fwAhjA~PB}w0 zj_l@UgSpg-8OwW1Ek|2rN{SR?SWYfyK0HCVEN;V4bv4mNHpWJPyor*`2E;_@7_PrP zFTkdZ$%a2$)0I^kI|{cZY#ortIk1~Q!ohhL5#Fg@=D?qU7TS)CUqp}s(|osuIX zdH9uqf1sx!NBZJ`9fb30tr|KR5t~c~?Gl}y^X!O7(H+@-FQTvKi(MOD{FYO3pl^_q zQ_wFG8?VjZ8m^H|R{eo29Mtl=IW87A_=&F8MbLN3<~=jRuXFAU!ce>&nN3MVj)B^R zUZw1FHZ>{f4mVAFXJb5zdHec`ylCZiH!UZn;jy1A;-1}BzDK!*fQeGe+)kYvb!1cl z#{pVYhi2U%>89$hWS(l1GmMmcUhK!A-s3grA>F>H!HFbfDGDD!hxjLnlYy2@URJhe z!br#IwZ4`|9pHGTtn4?u*4k1ZrwSqXAxC_jqax;TcIjmoH_4R-9tUqMU#@4`#6cAC<%`7MM8*0KQVVce=V?Yx|U5stJ^7@9ox#QcU$3wgP0ANyz^6dkYOzCM`JUQ<=nKQ@}CMaRt1ZkZ3pyz@g93?V!6Yh-?I zu3fE@YEXlfMtvk5>)Y0LPRlU@T(_yNzBoRTM?39&TYN)%E(vzPJpgl{N|FBXdzVeU z)hBeu^HKbZDZ8G&gDRU>-$FVTq!NvpEIOa%6gW#hlZ zy{PCxzgT~E%~sUXZTt(@L%R1hGjnrE11u!*oURt2mbqFZ>epfa*s+kWQa-cFVu@Hb z?xyEt-RwNcM)LWBMU3>*xn6v)AjZiT*g-bK@Mn(e-KthMtD#Cz?fWi=wV_N;#mJi= zMa<{0|1~TFKv5=MHwlkDhvATh$J#hUP;1Q@)f+YxD^9Xm$RX(lnuBl^xI#5Uwj?|( z0;BOM$ef_p8R4kRt4l%~{W-7&1=34ZRaNjmXjR!!JG1Nl@_`8+gj^2&D^D||lZcoM z(GRTtCh}Bt@d*5*=Dbf!OKWRmGYaU&mnTe^MBK-FIQ!eXsx>xw#i0+{He}V5gu3FM zBI#OK5GlrJi95tV6HJQr3!!sVmz^*&<#~)i%R5#h6m6x(d~ciJC8V7M$*35TMlA+=?o zJZL#{M0Y*&{>m0p*}7ew$HDApdwj&T5iL)Rnd<$W)b9%ko-(?!vLcNHpj#? zG|!@AaWm5ErT?}*U(sek4W=vY?b$Rk(UTD4bvr*kHNSHp!_I<@?aM-&g>h>xk*9jr z+Pv+Tvz*-i*(IBNZ?A${X>L(6P%FikAuszJlAk%I`SlnH7zV0ICOmljm{K%DI)*!| zjC0^=C(V5Df_pRSd#^$eua-7`@7PSVLBBOZoAT}3bW;wwO=OcE62FOy%jS?}NG*n| z-?4_7p;Sgt94+k%kP;TGK71I#lvDf!s?QYL=F-2{qk3Ilxa?_XEB__ZxBj+Wmi7V2 z^8AzWZ+{F84UqX=goFgq(5}|6JN|0NEmjtmbi;8#QiUWXyZ-!<3NMq4Wv;ZE`H;0z zKEezrmM_*&O*al{zs@zm$l&gNaeVA#yN5^l2MH0@#>~Xzy0_RhkfWR-5%YBZTB5;{ zPoKMaZcq;Sp9tsgAu}t5EhX_p&glD)#+eeMp&pCLc;KGU>ivLT4oA5={T<4p zjP)LI8J#7u+z$O{zFlLW_Mo_JCSvlr3x6HNf^0v%wtrzC-BlicQ{dUhr?VF{q3~dI zb@L-P_~+kVo|Ur%t;_z=u4_-a+wag*F52UM3llptH#cqnRFxT+Gc`$_axCI{^%&I= zP*kxh%cb05VKzx*mrU+48XrX#P*okdyX46YhwUf{PL9SUTKv_nAuzU}l=(yVZ2R!w z2QG`=YTE6vccX}(g{0oT7+Ir@(>r0AppT2^cJtC7&kBPw@lFAMRPI56#Om3_?KWZo zzy`L<$dyAZs1nNDs`c#FdMlgx^7&?^Yl~#(6y@YzdYm7JgxoXvlMxZ22*#|#%?a0o zRYg(^;Xu4n$1N@Fx|-Jj-`FmmxA8>cVUBy;V`0FUMMh7?yN!kRt}|LzL*pEP;ZhTS zwwr!L_o@=PX{f0iG7Y*V9uZ*rc77BO3Z9jAUiIj5?YfmgOQfa1uR_M62sO&(Y_%Px z^T`QFPOge+saLYmWE9_^ARvM)?l|>-!^laS*EBH!M(#5C!TkaWjvC9O8Taa(s2o$r z7+sdiG8P{@&e~IEwc-TM5wG`C*ukcZVWA)CgwNd}qe-RS9-IGGQ*+K{pla6pI_L_8 z%3J&#?&{_JY*~#bE7|X-uwr7s6~UC@e$?2KGe6&%YZf3&##u5n&FHzukZ!QISemy0 z4dP*`R(Rlw>DWjy$fhp$Bf>%RhEzAiX=40|&;OCl+EpB@i3uWP|CHWyX06;3iGMmG zH%I}O&#?b9#Z*_ZfI2mtDcjN3R#Z~5e|Tuo_Z@@Uc&hHg{~?vb z`tZ!!n(Nu&#?jW)J43_XmHjgFF%u&rK!2rTnMK9ds*ldW=+zR4uY7E8z$lP;zk&E% zrjbH_Lt8-LhAL-tTpa!p_Q_6N)$s7}<^;+R-hc=wo(<|6B3&x3wq<$4IP7PS;lrb= zs;(x1j(}VdIp(D@J~j0z|J)_i^_CNr;@CuITb)>I+V#!>G~7T07vEcZm#@JWH<(BC zvi0@1=g*Jp$|4O9F7WquV}|mlyplq~;*O?eE1un^IYAk}o5=H5DqS%LBn|Z*ds6D+ zrr)eRsW~UQ8gn3cAP1f;&Ck=T8eA=i@BH1*V+EIn)EAF<+zWQn-TP0>m6YV3dcVE9 zsD|w2AS`*cmY61Gr=mFN&^PlI6b<)}g$*gyRmYV0?5Un-mSM`YNtwQ8{a)|f_qTB` zHpGNE87E7sJS^ySBQ8Ig>C!wd`cObrR#pU5&{s2W5&T{8xq(_#gdDf#)|)y&=R$gI zXl#Uoa*TLmN8jx(f&6ju?rr)R{QO5vN$zh`o8cg^A!Jm{FaG{Bi~w+!6cz0p9tH&k zU4ps5!~_;*xzcut)ohsL9>!$7rzh|RV7;!lBK5)h_-Xfcpo~2b2>JQ>EQv0s`_u?J zjpK>cEXS?MQcG>Y&AMN|1bBFArv|&WH0*3qy+F);vpwPwwr?CpJ`PYpU1L?V0d;1b%UYGR+SHN9W>Qt;uOf?HM zum({55_zQE9$?cyANmSS^1J6ACPXXqF|6P*T34pm1|@=@q?nkvGMAa0vSsRs78w=W z-%0BL*pd=CZ9r{+h$;0G7Cw`WQ`Da-;QotQ4vBCyJuB2*ctG{bi|*;Rz3dMHL38>p z+WZWMExt!j_Ur~rvVfc(q1Vova`Z~Qh+UDVlI4^B!c45TExPS^1S(!P%D&-DOAt+= zNZl5lqZUf-`Q2nX#wGxFm+|a_`Z`ma%zvmj0#4UCig6sT6@q}yjG30fB@k&jjk z*vHNMw#%*+<@PxngyqrE!NoBgwLtQdJb%VS>)KOOS#Kq`pNP!hD;>Yu*U^1dfRDwV_wobtdkS=adKIE;Tl%uhLhkiqi8 z%CWMt-pBy1FE0<}NTQ(UKbn^e&6R8Ma7SGbXYmLDv~1_Ge3q_IkDV;}zqoq`gm^Tx z)K3mpG%rjZwy2KZMz304eBqI+bL)-uj^*2fPZJpD?%Ywi8jkA<1KShd?Q>v|$t%j0 zuLj(+ZnPCu_A72zf4U4z2a~wvOZPkae;Qy?9c+)UroXG=X{mYMs9LA9YgJy zBRXZSy|TFS2DA0F6LPD9sLJtMSx&3n&dMu|zhM_sLa}j#d?f|`#F!aE5uwM?^*TJBh(YpjhFH zcwSbL8-Eq-C-K(f0d`}(M?qMbL&xK;obrgwxmI$%%lhKVAMaRA2hAY+j>R9zQpB<4 z=Fm&bT5|n0jLgmLgdq?KWm`|Z+JVwS*f>v`#BVX=9Y4aLN7CjS z48WqOJW{EDcn@Zd#21dQjx01!aO`gH z@XmRAOd&z{R*}dpCI*6X=c`vLHg4h3(ZiqU$E4#OB;D^%m1Mc8s#flF-{+{Ty!|z} zb@MCSJEJwJpWZ~=@Q8NxUgyXf6KyfR7)}0;EIL!u)B)Nlp>Zsj>q4HTfkk{}3FsXH z4P$$4v~qFB{K36XD5pa#B;=~LBiIKg6KbIh^jcGGNmxW5)um!)JZ3t>Rr!XOzl@S@ zJHCaH3OGMTOO$eio5ROz++djH+i9`$PW-dZlK>#6^0jN9i8X%OZnn@kMm2B!E0@U$ zjRO-Clel;XCL!lDcu-SYSB}FYn3#{(d6)e8^XD`)tI|hBGkWe9JK~=Fa?tVt z#D|5c)=OX9;$lK(kB|qc9?BM}--Q-G01lDBWO`a*LKZ)O!TRJIOpEQ#9jS`w_;Plz zK0p^Ar`U?R${Ds@ZCW1+?EG6&EXJDPa z5`KFwGn5n=wUCwubZwM2HgnBi{&cn*K7Pb(PQyq~Z?Au~sqT0q>zqgY@i1t4Y3FBo zJ<5YG92b3=cFN2Oi8;)cK?HbNFa-3{^0(xMbyz#z( zLV82cy->~2-o8F*5ME>`L1n)-Tr{^jkmIRd-%ZSFFbJhP&f$yo6VMN+DB5sfFQf!L z>`m7331-Ils%b{;UfAtV2DouP2_x`CV0Ph}b=pd=tJ)*nK z?^zBh-g2x=Qe1qkGlqCQ+-kL-numwy-Me>RZrwixkK2o?aCA*IA%Re-hd3E2yzoS9 zMTHM}0t3BZRA__IwLIZeeFn4>M(DWDz1sA_1X@*HP5bN_uG^ck#NdE{fWSa{8XA+S zIvzuA!AvO-4&Y&Acwg&auAauf{G83aO`UoUViW+rvM!eE|DQ5vbE10fA7zfe(C3aB zt4abbil~-3B(G>($uC0ieU4)~;q>nPEuH7Mo>h038N;Lg=4$z=3dwqZyuLDiHw2IX z&m|`~_1$(d1Jas|?tL;GD49l1X!V^~0BryhSzw>*nufq35aQ4Lnl*1j+b-^@64;?@ z%VAa(&b&c&xGVIJu)>oK(I}Tf; zuTXptKl7hGeAuYjbgd@w2_XSl$>|@EDFa65ZFv?M9%(T-lRGJ74xARk3M>GiN&wsj z$Bl;Db+$0~3wjnGkR$#@_Yv_xs9f@@4(bv`RU7#cgW&g>Y3 zVQQdUs6foKX8GfrrTya5HIpF>+^L1mBQ_UO@pyP0)9(V!?VHkLmD zCR~G26A1x`b(<<);4K6x77-?%&UmrGUDYQsP+FSo{(eQ-d|p0WVo!xIvpu)SH@2ln z2S!*4qT-Md8-UG#o>ZmH&)<%em42JPY**|Ol1)H#d3-c z(9hR@oA`yY^EYv#&OO6l;~`n9M;ZWmAk4W;RQ`9zvxvc!-IsKB=0KQ@RKEL@`^3P& z0A$C(M8Za%81)5(jz0 z0aW0?GS;d4^-)m3S4B)8#5vlD!o;<|-9{x`?_ZflGR7FNeAEw5reRdCa@wdm;Bnn2 zp~$A838R!;*Bw?D5SZO;#>8=5_KJH3gp#i74@H(2D0 zM=pE=C?2v_woGeUxz|)7d&ywIT#AL24YK*~fVU*tjYtQ9Q=v2u<;q%B^aY3C=g(?e zg|hkn;}}V6{n(#kV~Dv6lNuK~;|+MaOlTB6nl?;q4HhP<9q|s=Ma9w#7FN$jNqoAs z{Tg)6R~Ww4%V_b25y+fLGxPsjXPmESeY=Gu3=u*IxFWe?6FG_q;YWyAT3Y!A(w$?` zaG+@L81=x^@5wJRT%ZP%2dQPELu1QebcUxtu68d@k=lcw+fFs@?P8aqAjnPA_+~hO z?IBz8<)6!Q$qRcq4*AkF6sqy(NH`WJu(jDz_xgKJQ(i4T@vM!^4xq$vS)Z z=HH;t1W_&WgRY00_}s(H&)CVIa!Y)>T%-rxgh4ZR!0L`rXP_C+=UK!x~ ztd^4+FpvfA3}`$hB)+(*0G<#DA0sl7hK458OGZe@@VDQ6#JBWx7M-77@QP3)`j;0YchvFB!NP`^!CuZy?MyF{yGnSj8gd4aqfb zZf*wM7Yr|e3w!omGVZloEE=gSlszFL(1l&wb@@C3uop(B1AL;F#S|nyQ?!Ae4OsB#h;dXgG($y8{=7)AgU_6)`lbvm%rluw;dRwu= za*7vr1HXA zhnv&s;*=!|bV2?3np~s_@S3sWH)#V;>SfH4YcTF?3i>@eCcIDj)h{5xRlBkqY%vHr z)#n8zC3X!OaXpQ`vS*Kja9QnINggJ}l?R_Yj{X@GQdMp97}({U5O}kInzTIo-~l&L zLSsG&mT06bVz}ONYFU;HX}au+5Ntv4h)|j;s|yK9d&>+;aSSCfv38Kfn{bW6=ehLS z-kEEGTnlD`bTPPQCu@BxcJP|_0dS#)oOXch?}d|(DR!`fTgIyFX+5t!mje*F6kT1< z;D0h`R1_-aD8YcU(t{d+(P))&zY)o`z}H52EiW&FOdg3}M~8%ZXmF5n&PU;K$ym8% z*c0aYR8~sLPu$EbEZ0L0;5nq}E8=z1{i6nGAyI_U@B_pcGEy}9A357mKf$BKd4raG zi%Gx;;`{8Z01p(-FbYLqlXGTfrpRe$b~F|s90Ej@UDm;#3+UV7pNG$YWbFL=w*V%r z!U=mG?x${JKO=p#;P?$1e?LE5U>y;-cz7TShb-7@RMOfif{cs|T+H_M46zvnb92Pc zske(V-}U_j#4=k$LXB*CUW+RDg$Tti$x#~e^aqNl8sj4cxp`G&Hd=2{<;E(_)+*0WsnUF>%uO@87?Cf$?CjBKUUp_Rzw- z3H;&z`7^wZijoqxYY4?hpxa^BnQBIrBN33kCcGPjPtxCDWxcn3w|C9NG?Rd-OJQzt z;o;BMFVRT*^Zoq%ws&_w`T8m=E3?&cTF<^Vxy%R;e{Hw=H?K-MMTg}s+2@TKvUw|q*uxf79B=Q@B~1j0Qx^wDDd=b zt3K;zDM-ASnD&N?-a;1+o`UkiLZiQ+^C!fF;?|xUbbZ0vg~`cv$#r$f$%zBq{FfQx zsXa!JXf(z(#>X`(Fn9#eN|lL-h$IcQbQwX9Yj1DvVma2lpsTB^z~8D@lo?M8*PI^J zhul3gVql0{-;Oh(Nl!LoT{Tzmp9c~@P+hQE>g5&`qLM&dLCZLlrw%?196Y@2^mGbF z#=nacz7;{>shI(eK1JykYE(#(BMfIAjHPO z5psuaJ_(X2BMnWv>~SA;YW@2rJ?ORNS`a_2#kRM%Ls&fY-%ou&xeNXi*ru^#AtB#@ z02UB{c|`qg{f~!7M>)B;s;jDqxSc|hlECP|$H0JzfTe&t`Rrf~&NWh2Q916T`wqG1 zy63v?!0J~|57yS#Zc(puN!+WetAi{H5zAti18SA_)>fUVj~_^?DE_b&~QM8FDd4M#4RQ!29h<34DhPZJr3YR@d7l(rn|cviUD^B zyS$R;JJQlQPND#N-Zw&0?G+Onk&~*<3dghOK;h+%O=50l>hY97vh~=UEs=@UtDB$+MWhDxW8Wk z##;iZ4T7hhm3^?_yrka8D%v{Y4=rX^m4y1{>hfZp!WWYe_=vkW4Eom_5vCDPa2N_f znKCzrjDYoo_1)augv$%>TWK6G|6Ky|6Nzj(*uFZDbd{6_AvcL;)&Twnn*pNrz|asf zznxu4T%0->KEUQ&UVa3rNlaYaGhO_iKo<;i`o~B!)eS_ItE+4AgDtpGAx^{fm}?HS zb#TB$N2deybgX;}JYr9uK4o{nF{_37o`9{|tlLcyu(6dlyDEf8k_}5At&a#cFAxwC z0vHQk4Ww(571|5vn{%_WPR>tk!~SkT5Qow)v)OKAG-9;3P!#zV`nSFnuyVlzZb+C# z2}=fXpw@YB@jWtXYnnU*Lq3P?A~-;NK7C@)uB%}o4s-K)Tqf~4GYm!s{QK#z;W^4M z(L*1nvqOkFoQUcMJ|p8@fmZD> z(}!X%_fzJ8CRp01jovOUF8%zog}RNI)*YRl5;ld#eUz}=j{_8ym0KDc<<-^CE-%ht zX**Q0zxBYJ0#c!S9TXjm5*o1uHe;C*%ufC#S_f2_2WuR>OUlmg-y0 z!GAQEt13bH12#O}V_H(u_u=^X_}Y~=>22zGf15#D!NkmbzPlinD2g;{@`fFWQX{o+l zIC(@2yS1K$1vnC?A=q6Tcc2D?RfU*V4}XB?hkz_*1C;>>a?pR^h$~0se2t!gfr&z0 zWR6-;hJXb`l(q_XGi-&>UHClfW95vjtda4>SlFx%@Zqj$)ALD4Qc!#%_(4|!i1iE%`va(Z zA?<6_xmA{x9l{0H|Cm-@?r34b(8e4V6(zjkpdu^#s&)sun5?XJeXQA#yqAvVmTdxh!okULIzN%!mRLNPu$>oyH|HX%G3En zI$qKuPs^Ax3ltL^c6sdUAGz+aSIMo~pI!g$^2|yV=k%z^QpC>_S+@Ek*}vT|q-D{6 zJ$BcdKjD$olk3)8hUyn@09Nu9Z9L^?&YO_g|M^ulg9X}vyh|tKgX~6ITU%NA2-1!D zC<8?Jn}9}KzYQlfeDkIN9C2}RKIh$=2nhV;;7X$mi;0VaJ-!b&1~_Bm@plRC2Peu`x9Q7ayOBoIE-v=H&EL zDvq_QwbjoBd~=_Hf=CwhMewVuSLf$%!d5UpEgHQ)?r_5%?LW7iy%3l`=Vxba937{s z>~qr77eE62=JjhZi^3H`(gCwBY;UmEA!JlkL@Zt~GfPNIXC!_D{G66n9mYaS!DhXH zp^mJl%s>H(>5iA1o@z&-hCIYZgP|}dL+=MUj z;e!A_KSEJYM+bqxBH|_i9eVM|$cXul@jZchtbd)g$x_15zlw;!!3Drh!Z|9?y1`&c zSy@slDq$fM!2DneE8c?#fW9s+FQXx@3+^5xsHx`rhlf>!g`u8;0yCET2P_MWg0)k^+c!2+y*u+O1FgSZ3S&mabDs{TzG+6w6Eo|2M+kMube)e?YU z{}u%mE(h6A)B$M`6od)WQm-GY%gd0*kPv}FaeSV<2y#lw@9F8P>gspz+<}#c&J^@} zHxO)WZ1BY+Bb8)ix*!?9dbvg01MM6;&@Oym~m@y;N)8jh8j>zjbOY#r7{ z6h&0D4dUP6zyQ2US4+zhSYGvo?+8GL_vcU0#d}y^xKwnH*R-#m!JdV}a}@#QE-YT= z72m}*Eew_IQfJKE#zrmpRi<3CT6PsNFmf-r%D?vY4_QVQOceg?YSQm-ZH|bMpv3x% zb;X*V_FXcG#dYg2PpA=H?W%XF$og(nE<2&sYgO5z1IJpAGmC*!$+)3BC1rW@3r&QK zqNe6qdKnALn>W=q;ck~29{aC#psD^eKs($)sIl}IJId^6w_j#W7@OC9rx+%<&D}2U z?X{~4iTW!=%qacf4NN@SadG-BcXXnLj4CsrML{VUGh<}Hp_{XGR1n`qCE3;SxBh`! zRmKOmxj|l!)7+Yr*@B+b%ut`Sd0?3jIe*!m7ABucdJLcSU{Q^XcC?EC1C=9=^EK%} zQaoB>G3WQZD^`jtEv@PormKymlDhI3iAC?|767vdq-EBI*vQD%&KGFU9Gv=$m=P*f zCeWcDsE|x(e(rDUV64D%czc)|lXe5)*4ZX1COYTTFG42B%UjnRc#e98k$i=!!8tq( zqXDLBoNWQ5focYOacK#v_8ZU{PeW42VbBoSpjDCGY4mJ^5?fSK?9H1dh%_TfgKYqm zW(tDqx6~0f>(S7-b5J@Z|C(qoUBAepksUrVm__MhE z%dYB!8`fdFxKf_D=)xD;@6rjq!s$l~-Oq2|wod9wgsMFs-vbB)m;n|l?oFeN8^jnBwshDx`SC?CT z@7QUux@8=TlX`ZMJ+sPbX`d+d5#sSRSZ?KuD+_Lo2jKDiGWwDdU6B%Kkx4 zl)SxRXDs0n4e1LGgDFhQU6huIvz`E@+(jn^v(5`G9&GkIS2a_>=kmTR5A*fC_GwQ} z@^W(%AaFr+E0hA|WlGA2QMnH<4Lfn6(nFx#z02jaO>#zY%ex2=fYDJU_W{3qCR7j- zHegKi0@QgPTa%#3#Kw+*Agf*b;@L4bLZUn-G)kKvJN65n^_mp9tG^ebK00$5fx$v1 zh`dyJcSYbsf2KlzqcDQxDP5;l(!KllAAj#tcdCwQ3UB@xy!B>ZaRmbbm%m`D?rr@( z>je)&-|$_4|HVB|#YY$<|J4FyKcoGsz1BfXqp{?Qi{7I>y;y8MGzr%c$NL5nzhvo_ zbahIlUs1+~a|X)-ikv|PjjCDM`8OSoRvBzf`V_66>*n{*gBvcZ;G0RB~id+gWtIC(~wwG z)3d21rAX4XH2T(#s}0S$g0dbNKB)%T%QI>j%}sP6SMdf>yz26>95|`ipSg~rB3hjz z+1xru!QGCX?o_rVz~w+te=04v{nMvT7b6;PoBkOqpU;%qk`GoFbHj|gF?;0+@$l|H zmz0)kV|hT0qK91Jmb2VBSl?XF&x2l^_L=}aejVcQSxsL&6Sqt ziDHwr+$KLN&XH#vH}~u9SWHWojKUPCuF65%LGqE^=mzHIFf4rPXMMnhWIg8*-8-;9=)0V4xvQ+C1Q-iRpg6pz_vy!=>lO9} z5sSx9g20_uaxwCX7ofr*+eOSmrnAZ}i6Sf_@(Mt7sEjRkqal~L?6}V3<>OnQD9?ZI z;B7#L4nqaQ)Zpjf4*Dig)MgRD?M!h~=$qlT-s4z1V{T3S)ZDUjib0aps2eS{z z$!7|^Par^#cH37vFZ_V}MH@zR=iXJsJfSYu1eb-igXt>_zao<<24`O0AQ7VF^?3?* z#)+3-Oy;T=S#-J;LKEps`Ak#T( zGV|Fky&xqT!lkwC*1&S5^U-%g<^xsLEbxVQ{e_2k(Aps;E_T(+S3@Y%G8FNsYuvPD z`f50a@5$s?K3>@A^SD9c-kH~<1wWFL$K=?Z$a$1CG+h4tu|X6hm~Z{k4~mM)cobhZ zGGcY`R+KuUpd=avaqPmk^gJG7_f4^|p@uZRs z1ow76P*MD|H*rrozVilS3P>Dg>fA}77AGZ@*~il=GC(6ICvPJajld7p7b|;@aTAM_ z2Mo2Lglw zvTsoo@O=hy(HYw2>bUrLZ*T7b=x$Katgf>R&eye(`q<)DzO|SZb9NH;LyS=^z1AZ; zbu`;-gjti@O}mr!{(Tv&!=s}sSSNC2rMiW~?Ay&lnojrIUXQ+>BWH`Bt|TwD$?j=D z5gMbnnYR`#_2?>7eNxwwP1m+^nR6VEmnr;=y}6(=7Ky@u|;9+P0>qJND}$s{4N5`Nmg|JwGB7+7a=+qr`tAx|Lzq{{*lTU_>PVZd`#)|R9bKn4 zF#$+Xiayi8WX9@XKl^7Yrp|Pa&d<<+z-d7`IrsPiJ~+w=$_aiiv-Z>OM8Q7!nch;f z$kZ=`UP@08U~cfJvc0M4I^weewm1g&>a@1Br2HoNDmPT3tDVsR5?OcaV`F3I=jKjT z*yyu!zpbirfnCwTfz$QF8^~#(u-e+(G%z;St#)MMt--Ff}z*^@h)L-J0Do7*u=s#<}{#hmgV` zNdE{6w=}tOx;neD&}yd*)(I$|e78%#^5)GO(>gb>uUCqab8@i7!N+-&$WdDFA_U0_ zqF|#}PYMlf>WRjnHo4$pNVwWl2U$+Ui;2f*x7>6K=xB#I@9(lXv^O?V&CL02iQ>I# zYiqatqJ)g>?NI zu5B7+cW+8>eKMhYLQN63{L<7l-I-a)jgJY{gGWT=y4x(yr@eLS=XEN7>Svy~;bAGX zGcu^=?3Ki`Mf>}kb?j>+z6wa*MmUo5RLpGrIdF0(={9`+X&}{KO(cCnCewJhrB?dG zrywGMAAg>^JmzLetSwKMDY~1=i-YKAXFE7PCL+ctOfY!DEG79NGTgl^epzc~lhXk6 z77wvC!v}+S^;+}751S(_g7&31rz#0c?e|<5USB}&OgTsf2y)uOEr5gr(V@07>O4HM zeeENE0_C@9{qMQCwz{3b1;FA72|>lHqpf`pVbmR036xNCrXU&<}nFL>*Y(AFyEd^*K)DiH_8zpqiSx_<3mpM#MF&%*l4Y+`R{;x}GwT42Rkz zIW2E+-h7P^Rv%k}%U!DEE57jP%*c}NX}v4(<3o;x*+T|C+O<7RBnM0V*VK|PDRScz zVCSFkxsRtb#iybo!2W=hD$ua%+49Qr2mgp-OGld?A{HwBsrC!LD~hnT_S8EXMgzmt^ye0@wy3Lb z-GCl=b1&cH-AtiV6A#%eDeoV@f5S7MT98q34Iv{Qj8X4?(E3(jl~*j7Y}9pKQBqF< zm$MT%5~J>@h|uHX@vcqu$b4FAnlPDEs1zYDpMVbY{ew*UFj}Muw&F^9)~M23KR-Y5 zl&*w@M;1tYMDK9FR?SQ1quY>DvktGaAVv#~FXCMl)VkvV@W1A@icPM0_&QGx3*&}p zk_0wns<^nnD7_sn3d&{015tTaS3G}HN0VpgXNwYSH}QK^!M5DnfU;T}AE~>!+w=@s zk{4w1Rp>>OT;j%J(VOoFTWytj#c2G|ooS*GV{fEa%V z(T%9Nr3KWT7T|LwEACBA!EZe)Y&QMoc=8DD=xl>N7go2j-p{F4s06w5b!7?h6xXt^ zAYk&YagXlqc-;!rjiXmZeyh9+?WT|=lyD72q*bK)Fh9NNHSzfX@$v^xar1_Qjs0Er z{)#qQi%Fy%|0ah6@m+gI$ZTYXQ`4}Db|67KYc~v|3rgg-;io71n$)e39{SzdPPhMX zdYEdZy~=50e;`H#&US{tZFpoPtTs#1Lm7Z41|{A=@f5|ZI*%PyEt!(6EMmmdKfASp znjM0kI(3eXZ|s!x8}(ALq61;8FywO?TsbsG^s4Qc6+jgHb<{EH7n}(FJ3c9_9K;qK zzNPcn_CY6j`^o}kO0>_*qF(YSCyJ*`1qY$=IsGyn#e@z=_^bF!^$!gMyKeUSp!M!J zy=PO|6f>ev>Le_6&f%^itXUXhR_`dt%a4lm$hTSHyY7)%P5&hB(_OOwj)d~blXELO z&dfTRS|$n#;c#mk8!3k{>a#`up7{CIbj1_j`hEe3kuz#3hyReyfs8qqO& zs7B30nw*Y~fJ^~>V0pbm^-km$o}Q8!WM#zJehIhLz;um(&1iX6R?O4%_hL;597lGy z1-t|Lu!kHsvE)@(hhic)T3kZkPX|l!piuB~T6jkuj4wRn7%SVLiiv&Nt~d4R8o;Fgfrxx8KBu=;yYpjqnL6&R zTW{jC$NL=21j4eiyA;cfXm$tjJqMR zv5Zs$Kb!4y)AQ4_^V2m|_oL$=R%ptc=_yLV63}4x&dq6!Jt~1NXo^M8nyf2B)-q zL!U}QER6baw453gl_NkT@Ow&e866HY)DiX_T)TECB|Rj1-lF*jWxF{-z~gxJweZ&+ znG=1_M1F_Wg7{(*7MAC_cfu2=TYvSq#@<3b$=s*3Q_Y-?2>%QO=XC_`uQcw@x<%H4 z&(OV3vgx6uid!LomcPBvVPB1Gv->g>%|k6MD6-*8_)Yo zngnm#ZnqE`*Xv)X&n}9nOcc0WZ&5MLV)(p!+V_4y_p0|$2SJO#r%$fH#InBqGAbJ@ zEj@bB2u4m-pg@6vvV}s%k>!z(;ysph1_AOhJ$Sdch(SV(VUsv_3-Pgyo;F1sYq*cn zjFzzbOfX9(kP#}p{Dh}rV7IoY?yd;*GxT?UGhoW1Q2gET-`!h}CgVdDxelBUY*DST+Wu z?K}g?78dyj`kU9syEF;rI(et>57eGCeHCVA)<9>{W78bZo1J0(IrIKKTyn;RAC1&aRHg@XuvM zaifQA;TYn70%HeR5%TF!R|{FZaCfg=lQHI1kG^i1_nojl4XM6ZD6HlxKex-DyA{0a z__9|WT0ta;iC}aX1f=Cmip21ovTm0Z=4|~ZobZF5^@%{zR`lhtS~MNkceFPaEFgOj*Wjb48G0S{w(wU0Ho0AL zvVH)j9}x*wHGs7M0w|vcbAfNATH8yYFl?S`K?2a6HYde^%O2Ke5FNMtlrhvaR9m(y z@{(}|z;~*1Y)4XVFug|?(E!7Y5e;2qeRf^ah}V|mdULNm0Y0QVu&U;L9IBiVcu=JO zD2r5UiCLqrs;({}IC!IT<{S#mAKw_;zP_@a-C7LQ%$q0)Kjm;9XM;Ty7guj)>hb8! zPr&WAlHqwK@6~Fnw^6hO0vu;PRY-zJ)+Y-)N;YV4-l-bG8n8&Hd*dv0tQQ8}UNa*O zbrOJ9Aqtx8rVyBWv;L87%=oTuWn7hO?d1C;mmJ{urQ78hkP;`SbGP_DfhkcqqZ$Mo z7!3%+1)&GUYNn29rn4Ipm^K5aPr5YBw9Ovv25eO8;$+zTm*|CtkP7z!%{;f!HaZ8Q z)Aq1xwg6}tC<|DF6T1rvX*C zCUy@H@`}eMGf7lO*WV+)a$3)Z8TSB?auT>-uITJ6hCZO)wyKVhU)ym<({hmQg1IK* zG0$@h9_{Yg*K+64&Ao&yMvd(*+@|uiam_9+3L{fJG2f3@B+QxXGUdSH{#_6zu#%Nz9%6fglV;H1m|0# zV4I}p#q89#D$X$nFx`cqg!~GGuxUL92M1t$kbt)$DExqNPfJgSisq^pwI&uG7Vy}h z39?+s+m*flWcimq-MT~ze&mFFP*Fp_Hm7Fr}0)jovzRTB7kPK`3G;_tK?*Z zRkQQt1t^N+AUYhB*G%Sg`4BcQG!7-?^jyaJZk71VC@Yb@bLUTg2w> zk-uPVcuP!-`S!Gj!0GVAo7F^IO7B>7rsr#?PV-r*&nX4nI(flWk5^90UAC;Wwey_V zqE5<_Wymd2-sjqNIOqU7o4GOH!E#sSNB-|Q#=cWE*AdiWvK~)_ly%cDb17zlGtz8fJiUDqVS!lRtmD)RHZY_G!?;|tQ1zv{?H~1bO7lv63 zo@BDEL>WaW!Ts}F=BjmJKzMDeuK_J^Z)$O-##V2$+#m~p-dC@*!8Mu3_eYkQDQquC zM~8V@)mVsH7mo|R+2MFe+eg!r_K^pRbUg_-Z(I-CvjV5u`#*jFP|nVE?*dNGpRoJe zjkh1AZ_JH%StIWvG6tYxOLU}{iWvBzqE2K|q)RhKRkt7kC|xioC+_V#Ljyz{uV=lg z75V@j@Jpg|6F$A8M*aih4*uEu?hP5nibuKeAdLwdBRt7m5* zJyOe?dBZ^w!qNP)brvX`blxgJ?`vOuY~fM5y)PE)IsG?nFulz|o05?`;WhsLr;~Hc z)#Gvp#u#8LK`zL=Klf6b5fm+%+1Z2yWhEDy`i6#y;T$EB%pQmWn+M|e1& zuS@WMa9*9~@=QN4qN`q>H%)w7MHPS%ygGx45EYLCxw?dp&JC~f<&B#pfm~dqjz_Cf ziUZ@_pI2>0uIwK(q4L~$ns%iu2bhF!UxjI!bS4f~XUfc2oX>w$<^h|SF%SzfQ=133yPwwUvJ1Ulxzz8uJW{S9TUe-)E+EoEu()dF&zIbynIU{0`QRJ$Fe& zF058Ns;Z=HEoa{KChBt(k0?~*C5DXFACNB?Nn8+JZm`wB9D&B8XlypWsH6k~4ee;C zx}u^ILTBd8_?mh8`14{`Hjc08XoYIUMNt$?G@O!lvlLx#!|3EF=0vtUM6GOH_DY~&l0El)7`9Q8#@cV-t~8Zk>aOU_}9X!~0HGH7LK zdpk3XiNwnKdEdxDtrPn${H}``x{-X1ZkzB(Kgg0bl$GtPN1A(AxDO7^^~hhxEFt5f z_uj6m3A!Wk=YBORt0=d=q?MRW&B^$~+LOCGKnFRCALc!_SBH;~ia>%A_I(D_1o1M+ ze<0Rrxj3HmaxHF=?M#xUO~mmK;k!^xzB+x8Ra0IRTJ$RatlBN+R0|?__L<^@o31T# zNXes}8r_sV0DucCok|KyW;vBjp^E$kx>IK>bo2p3!&H@%ef1)14{Tm_h(m)k&_CXI z;>e^?=(6w%JIlxC3<1n}Da_F58Z)UG(;Yk@Ui1tEgzN$J?(tiybGxxJ?IUAAxF9jT zpao5jZYhACh|w|&uz-w6Oq^kvT0kPlf%*UTOmp1!9;kqfy)Vt-FK3*zypuANzgb|r zAPu@fP|bkAZg>zxq{Lk2cfsh7(&vH75a+yvq`cR$$%=T=1uc?AQ6i_^W8VMf4IVFR8b0T6j|Tg75&o<<2lgEI z-0>v3`_^fxHWX6POmi4FZVfK8l-k zbm>5FLYKbzMA=%oZn@!!r5SW?RDH{+cp>Wa`f#mUSqdl+N-7z=(9n9WPK)zy_qzHi zYDWm9i(zRA_4p#;lMqdt^;Go~Y*y3-^y_iAvhwpU!k!~Nf6oJce*L|etPT_7$!f8y zE~NdAP7t@gadpktC`EElAA)>~YRBxMjnMJ0>fHlbRg&cN^t?3b zp!&h##l;0R6*VO_vlS$$G931x?Kbz?!rC}_b zKUirBxS#jGY<-DD#suo%I;|H9eQ&q@F|fq4At7Xby6KbNCon&cm{s090x}1<9oX>Z ze;*$&D2N#v+DJ)>ie7~Q(AzYOC!*Sl^XpIktp!l}z67Z{^UkD1@S_;sseM?%FynWz zrxbSDalRPCj1L|XbC3DX^-qzg{Ur%@u6Z?lEml5a3%(BTF{y+=z}8sp#;-ZLCoRE_ z%lChHxLSIAA0T8Q3dkaOj7K>*2Fk-kexlLgT;U6PkaxkFQtX;t${@524CX-@r9qCV zczSvo5DlW&@n;zN=Fg|(wCYV)}VA2I2?X67s@dmBgmp?JU1BXXkj*cS{>v-e_ar7DY>DYAmTKTtx9Ryy2v?(hrdb?ml8lWGGl z^jnqGl?Ip#ixMkbk$55@p$i#_ma8WR z<$o7FC5)=L?(LDqy9etw?=GdY3e}*}gwDOXL?dfvn92d;l;>JuSDgf*IBcfCigV^? z8xPkzp9DPxcCR_Md;=aNsQz40B{m<`vjk>m9)+sO!≺gjf>|Jnef?+$}Ce3-Fqb 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 VW;yf9bwez1 zt)Fk#+B3%=Ox4QezCkqq{=FVw?lxa*x8L6S@-YnG$*H8e7aP7K;=WXMNtH6)*H5I6 zxp8&V{!Hh%t@%M|UKB18pW(B61rUWiGu>0zqoh|J{=U6!i-KM*`;vCl{JzH1OFp@A z7cJiE$b(!aO*Z{TYx}7S(8j1?GkEad{HsVwbO1~jEsD|`PT>egcCTjlT;B>=Dzz`^ zA0yOss=!O+u_EyIGk&EaFOsNfUcct5@v1Ka??W;VI|!HR)zrMAzRtXrxpeB~8oq-w8vk@-4MS@J10hZI!n zq@gJal`por-oMk$a@wMe6Mc;@pH|c)N9%??11n ztCK9_7+s@bw@~?TJqw}KwOwlb==5+C%UdF#6?#r?W^PU(rlu~ZMFc0YEGXRHAv8B+ zLWHFZlTX<#RU94Mgn_mJ3#@~`Km9?(RadWKKr{nRi#$26QzAE_D@f5-kxFF~SnDWK zix^LinV+2e9FZf&7rTY;QsR+Xnc1yZIR)~eROHy}+kQ7_e;p8>#uuo=Xx^9kEoNQ1 zkS1Gjp-y9<73BjSQDP(ZNy59)Y0R)-<|^kjg#C?%&X8pLox)pj!gyY}VJJjT&DzuL zr!*lMo|R?hRrBABQ5okUyhGXi)oT@4dr)#i(P1Vlw-u76!Vv5eoTCZ#5LY z4`(wdlxc6v-su5z!h=pQ(KV=9niY@XqTT)tHzp-T4&8`D?ntvhH-9894Dgk-7DLG< za}CPi@&aHtp6?v<28Md7L0a zXyYcM9-mxUF8;n@GR)9ECq7>A^Ijg~l)S|Xe@>S8VsCIj<1?<6sHmv7v6I$E7cEmY z6{jLAg7wzk={Xk%25tJFlzkx!xoN7yIqio;qK8d!OSwVW!88Kos`w{c=GwHxL^Vkn z>>wb~f6=_u%If72L1uK^)+Mef6{J(Cp;`^&Cu7m&T5jrh?XQX})ve8uY&x24Yz