Skip to content
Closed

pull #3299

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
800 changes: 800 additions & 0 deletions MULTI_SESSION_USAGE.md

Large diffs are not rendered by default.

Empty file added SESSION_RESTART_GUIDE.md
Empty file.
132 changes: 132 additions & 0 deletions bin/multi-session.js
Original file line number Diff line number Diff line change
@@ -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 <port> Server port (default: 8081)
--host <host> Server host (default: 0.0.0.0)
--webhook-host <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();
2 changes: 1 addition & 1 deletion bin/oas-type-schemas.json

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions debug_proxy_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env node

/**
* 调试代理配置的脚本
* 用于测试代理配置的保存和读取是否正常
*/

const fs = require('fs');
const path = require('path');

function debugProxyConfig(sessionId) {
const sessionDir = path.join('./sessions', sessionId);
const configPath = path.join(sessionDir, 'config.json');

console.log(`\n=== 调试会话 ${sessionId} 的代理配置 ===`);
console.log(`会话目录: ${sessionDir}`);
console.log(`配置文件: ${configPath}`);

if (!fs.existsSync(configPath)) {
console.log('❌ 配置文件不存在');
return;
}

try {
const configContent = fs.readFileSync(configPath, 'utf-8');
const config = JSON.parse(configContent);

console.log('\n📄 配置文件内容:');
console.log(JSON.stringify(config, null, 2));

console.log('\n🔍 代理配置分析:');
if (config.proxyServerCredentials) {
console.log('✅ 找到代理配置:');
console.log(` 地址: ${config.proxyServerCredentials.address}`);
console.log(` 协议: ${config.proxyServerCredentials.protocol || 'http'}`);
console.log(` 用户名: ${config.proxyServerCredentials.username || '无'}`);
console.log(` 密码: ${config.proxyServerCredentials.password ? '已设置' : '无'}`);
} else {
console.log('❌ 未找到代理配置');
}

console.log(`\n🔧 原生代理模式: ${config.useNativeProxy ? '启用' : '禁用'}`);

} catch (error) {
console.log('❌ 读取配置文件失败:', error.message);
}
}

function listAllSessions() {
const sessionsDir = './sessions';

console.log('\n=== 所有会话列表 ===');

if (!fs.existsSync(sessionsDir)) {
console.log('❌ sessions目录不存在');
return [];
}

const sessionFolders = fs.readdirSync(sessionsDir).filter(file =>
fs.statSync(path.join(sessionsDir, file)).isDirectory()
);

console.log(`找到 ${sessionFolders.length} 个会话:`);
sessionFolders.forEach(sessionId => {
console.log(` - ${sessionId}`);
});

return sessionFolders;
}

// 主函数
function main() {
const args = process.argv.slice(2);

if (args.length === 0) {
console.log('用法:');
console.log(' node debug_proxy_config.js <sessionId> # 调试特定会话');
console.log(' node debug_proxy_config.js --all # 调试所有会话');
return;
}

if (args[0] === '--all') {
const sessions = listAllSessions();
sessions.forEach(sessionId => {
debugProxyConfig(sessionId);
});
} else {
const sessionId = args[0];
debugProxyConfig(sessionId);
}
}

main();
39 changes: 39 additions & 0 deletions debug_qr.js
Original file line number Diff line number Diff line change
@@ -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();
66 changes: 66 additions & 0 deletions demo/server.js
Original file line number Diff line number Diff line change
@@ -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); });
8 changes: 7 additions & 1 deletion demo/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Loading