waengine 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +911 -0
- package/examples/easy-bot-examples.js +186 -0
- package/examples/multi-device-example.js +253 -0
- package/examples/quick-start.js +10 -0
- package/examples/simple-multi-device.js +42 -0
- package/package.json +67 -0
- package/src/client.js +412 -0
- package/src/core.js +79 -0
- package/src/device-manager.js +404 -0
- package/src/easy-bot.js +744 -0
- package/src/groups.js +156 -0
- package/src/index.js +6 -0
- package/src/message.js +628 -0
- package/src/messages.js +80 -0
- package/src/multi-client.js +374 -0
- package/src/qr.js +65 -0
- package/src/utils.js +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { WhatsAppClient } from "./client.js";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
export class DeviceManager {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.devices = new Map();
|
|
8
|
+
this.activeDevices = new Set();
|
|
9
|
+
this.config = {
|
|
10
|
+
maxDevices: options.maxDevices || 3,
|
|
11
|
+
loadBalancing: options.loadBalancing || 'round-robin', // round-robin, random, failover
|
|
12
|
+
syncEvents: options.syncEvents !== false, // Default: true
|
|
13
|
+
...options
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
this.currentDeviceIndex = 0;
|
|
17
|
+
this.eventHandlers = new Map();
|
|
18
|
+
|
|
19
|
+
console.log(`🔧 DeviceManager initialisiert (Max: ${this.config.maxDevices} Devices)`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ===== DEVICE MANAGEMENT =====
|
|
23
|
+
|
|
24
|
+
async addDevice(deviceId, options = {}) {
|
|
25
|
+
if (this.devices.has(deviceId)) {
|
|
26
|
+
throw new Error(`❌ Device '${deviceId}' existiert bereits!`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (this.devices.size >= this.config.maxDevices) {
|
|
30
|
+
throw new Error(`❌ Maximum von ${this.config.maxDevices} Devices erreicht!`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Device-spezifische Auth-Directory
|
|
34
|
+
const authDir = `./auth/${deviceId}`;
|
|
35
|
+
this.ensureAuthDir(authDir);
|
|
36
|
+
|
|
37
|
+
// Device-spezifische Browser-Konfiguration
|
|
38
|
+
const deviceOptions = {
|
|
39
|
+
authDir: authDir,
|
|
40
|
+
browser: [`Bot-${deviceId}`, "1.0.0", ""],
|
|
41
|
+
logLevel: "silent",
|
|
42
|
+
deviceId: deviceId,
|
|
43
|
+
...options
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const client = new WhatsAppClient(deviceOptions);
|
|
47
|
+
|
|
48
|
+
// Event-Forwarding einrichten
|
|
49
|
+
this.setupDeviceEvents(client, deviceId);
|
|
50
|
+
|
|
51
|
+
this.devices.set(deviceId, {
|
|
52
|
+
client: client,
|
|
53
|
+
id: deviceId,
|
|
54
|
+
status: 'disconnected',
|
|
55
|
+
lastUsed: null,
|
|
56
|
+
messageCount: 0,
|
|
57
|
+
errors: 0,
|
|
58
|
+
options: deviceOptions
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log(`✅ Device '${deviceId}' hinzugefügt`);
|
|
62
|
+
return client;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async removeDevice(deviceId) {
|
|
66
|
+
if (!this.devices.has(deviceId)) {
|
|
67
|
+
throw new Error(`❌ Device '${deviceId}' nicht gefunden!`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const device = this.devices.get(deviceId);
|
|
71
|
+
|
|
72
|
+
// Device disconnecten falls verbunden
|
|
73
|
+
if (device.status === 'connected') {
|
|
74
|
+
await device.client.disconnect();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.devices.delete(deviceId);
|
|
78
|
+
this.activeDevices.delete(deviceId);
|
|
79
|
+
|
|
80
|
+
console.log(`🗑️ Device '${deviceId}' entfernt`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ===== CONNECTION MANAGEMENT =====
|
|
84
|
+
|
|
85
|
+
async connectDevice(deviceId) {
|
|
86
|
+
if (!this.devices.has(deviceId)) {
|
|
87
|
+
throw new Error(`❌ Device '${deviceId}' nicht gefunden!`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const device = this.devices.get(deviceId);
|
|
91
|
+
|
|
92
|
+
if (device.status === 'connected') {
|
|
93
|
+
console.log(`✅ Device '${deviceId}' bereits verbunden`);
|
|
94
|
+
return device.client;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
console.log(`🔄 Verbinde Device '${deviceId}'...`);
|
|
99
|
+
device.status = 'connecting';
|
|
100
|
+
|
|
101
|
+
await device.client.connect();
|
|
102
|
+
|
|
103
|
+
device.status = 'connected';
|
|
104
|
+
this.activeDevices.add(deviceId);
|
|
105
|
+
|
|
106
|
+
console.log(`✅ Device '${deviceId}' erfolgreich verbunden!`);
|
|
107
|
+
this.emit('device.connected', { deviceId, device });
|
|
108
|
+
|
|
109
|
+
return device.client;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
device.status = 'error';
|
|
112
|
+
device.errors++;
|
|
113
|
+
console.error(`❌ Fehler beim Verbinden von Device '${deviceId}':`, error.message);
|
|
114
|
+
this.emit('device.error', { deviceId, error });
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async connectAll() {
|
|
120
|
+
console.log(`🚀 Verbinde alle ${this.devices.size} Devices...`);
|
|
121
|
+
|
|
122
|
+
const promises = Array.from(this.devices.keys()).map(deviceId =>
|
|
123
|
+
this.connectDevice(deviceId).catch(error => {
|
|
124
|
+
console.error(`❌ Device '${deviceId}' Verbindung fehlgeschlagen:`, error.message);
|
|
125
|
+
return null;
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const results = await Promise.allSettled(promises);
|
|
130
|
+
const connected = results.filter(r => r.status === 'fulfilled' && r.value).length;
|
|
131
|
+
|
|
132
|
+
console.log(`✅ ${connected}/${this.devices.size} Devices erfolgreich verbunden`);
|
|
133
|
+
|
|
134
|
+
if (connected === 0) {
|
|
135
|
+
throw new Error("❌ Keine Devices konnten verbunden werden!");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return connected;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async disconnectDevice(deviceId) {
|
|
142
|
+
if (!this.devices.has(deviceId)) return;
|
|
143
|
+
|
|
144
|
+
const device = this.devices.get(deviceId);
|
|
145
|
+
|
|
146
|
+
if (device.status === 'connected') {
|
|
147
|
+
await device.client.disconnect();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
device.status = 'disconnected';
|
|
151
|
+
this.activeDevices.delete(deviceId);
|
|
152
|
+
|
|
153
|
+
console.log(`🔴 Device '${deviceId}' getrennt`);
|
|
154
|
+
this.emit('device.disconnected', { deviceId });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async disconnectAll() {
|
|
158
|
+
console.log("🔴 Trenne alle Devices...");
|
|
159
|
+
|
|
160
|
+
const promises = Array.from(this.activeDevices).map(deviceId =>
|
|
161
|
+
this.disconnectDevice(deviceId)
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
await Promise.all(promises);
|
|
165
|
+
console.log("✅ Alle Devices getrennt");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ===== LOAD BALANCING =====
|
|
169
|
+
|
|
170
|
+
getNextDevice() {
|
|
171
|
+
const activeDeviceIds = Array.from(this.activeDevices);
|
|
172
|
+
|
|
173
|
+
if (activeDeviceIds.length === 0) {
|
|
174
|
+
throw new Error("❌ Keine aktiven Devices verfügbar!");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let selectedDeviceId;
|
|
178
|
+
|
|
179
|
+
switch (this.config.loadBalancing) {
|
|
180
|
+
case 'round-robin':
|
|
181
|
+
selectedDeviceId = activeDeviceIds[this.currentDeviceIndex % activeDeviceIds.length];
|
|
182
|
+
this.currentDeviceIndex++;
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
case 'random':
|
|
186
|
+
selectedDeviceId = activeDeviceIds[Math.floor(Math.random() * activeDeviceIds.length)];
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case 'least-used':
|
|
190
|
+
selectedDeviceId = activeDeviceIds.reduce((least, current) => {
|
|
191
|
+
const leastDevice = this.devices.get(least);
|
|
192
|
+
const currentDevice = this.devices.get(current);
|
|
193
|
+
return currentDevice.messageCount < leastDevice.messageCount ? current : least;
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
default:
|
|
198
|
+
selectedDeviceId = activeDeviceIds[0];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const device = this.devices.get(selectedDeviceId);
|
|
202
|
+
device.lastUsed = Date.now();
|
|
203
|
+
device.messageCount++;
|
|
204
|
+
|
|
205
|
+
return device.client;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
getDevice(deviceId) {
|
|
209
|
+
if (!this.devices.has(deviceId)) {
|
|
210
|
+
throw new Error(`❌ Device '${deviceId}' nicht gefunden!`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const device = this.devices.get(deviceId);
|
|
214
|
+
|
|
215
|
+
if (device.status !== 'connected') {
|
|
216
|
+
throw new Error(`❌ Device '${deviceId}' ist nicht verbunden!`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return device.client;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ===== BROADCAST SYSTEM =====
|
|
223
|
+
|
|
224
|
+
get broadcast() {
|
|
225
|
+
return {
|
|
226
|
+
// Message an alle aktiven Devices senden
|
|
227
|
+
sendMessage: async (chatId, content, options = {}) => {
|
|
228
|
+
const results = [];
|
|
229
|
+
|
|
230
|
+
for (const deviceId of this.activeDevices) {
|
|
231
|
+
try {
|
|
232
|
+
const client = this.getDevice(deviceId);
|
|
233
|
+
const result = await client.socket.sendMessage(chatId, content, options);
|
|
234
|
+
results.push({ deviceId, success: true, result });
|
|
235
|
+
} catch (error) {
|
|
236
|
+
results.push({ deviceId, success: false, error: error.message });
|
|
237
|
+
console.error(`❌ Broadcast Fehler Device '${deviceId}':`, error.message);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return results;
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// Command an alle Devices weiterleiten
|
|
245
|
+
executeCommand: async (command, ...args) => {
|
|
246
|
+
const results = [];
|
|
247
|
+
|
|
248
|
+
for (const deviceId of this.activeDevices) {
|
|
249
|
+
try {
|
|
250
|
+
const client = this.getDevice(deviceId);
|
|
251
|
+
if (typeof client[command] === 'function') {
|
|
252
|
+
const result = await client[command](...args);
|
|
253
|
+
results.push({ deviceId, success: true, result });
|
|
254
|
+
}
|
|
255
|
+
} catch (error) {
|
|
256
|
+
results.push({ deviceId, success: false, error: error.message });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return results;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ===== SMART ROUTING =====
|
|
266
|
+
|
|
267
|
+
async smartSend(chatId, content, options = {}) {
|
|
268
|
+
const strategy = options.strategy || 'load-balance';
|
|
269
|
+
|
|
270
|
+
switch (strategy) {
|
|
271
|
+
case 'load-balance':
|
|
272
|
+
const client = this.getNextDevice();
|
|
273
|
+
return await client.socket.sendMessage(chatId, content, options);
|
|
274
|
+
|
|
275
|
+
case 'broadcast':
|
|
276
|
+
return await this.broadcast.sendMessage(chatId, content, options);
|
|
277
|
+
|
|
278
|
+
case 'failover':
|
|
279
|
+
return await this.sendWithFailover(chatId, content, options);
|
|
280
|
+
|
|
281
|
+
default:
|
|
282
|
+
throw new Error(`❌ Unbekannte Strategy: ${strategy}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async sendWithFailover(chatId, content, options = {}) {
|
|
287
|
+
const deviceIds = Array.from(this.activeDevices);
|
|
288
|
+
|
|
289
|
+
for (const deviceId of deviceIds) {
|
|
290
|
+
try {
|
|
291
|
+
const client = this.getDevice(deviceId);
|
|
292
|
+
return await client.socket.sendMessage(chatId, content, options);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.warn(`⚠️ Failover: Device '${deviceId}' fehlgeschlagen, versuche nächstes...`);
|
|
295
|
+
|
|
296
|
+
// Device als fehlerhaft markieren
|
|
297
|
+
const device = this.devices.get(deviceId);
|
|
298
|
+
device.errors++;
|
|
299
|
+
|
|
300
|
+
if (device.errors >= 3) {
|
|
301
|
+
console.warn(`🚨 Device '${deviceId}' hat zu viele Fehler, entferne aus aktiven Devices`);
|
|
302
|
+
this.activeDevices.delete(deviceId);
|
|
303
|
+
device.status = 'error';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
throw new Error("❌ Alle Devices fehlgeschlagen!");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ===== EVENT SYSTEM =====
|
|
312
|
+
|
|
313
|
+
setupDeviceEvents(client, deviceId) {
|
|
314
|
+
// Message Events weiterleiten
|
|
315
|
+
client.on('message', (msg) => {
|
|
316
|
+
if (this.config.syncEvents) {
|
|
317
|
+
this.emit('message', { ...msg, deviceId });
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Connection Events
|
|
322
|
+
client.on('connected', () => {
|
|
323
|
+
this.emit('device.connected', { deviceId });
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
client.on('disconnected', (data) => {
|
|
327
|
+
this.activeDevices.delete(deviceId);
|
|
328
|
+
const device = this.devices.get(deviceId);
|
|
329
|
+
if (device) device.status = 'disconnected';
|
|
330
|
+
|
|
331
|
+
this.emit('device.disconnected', { deviceId, ...data });
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
on(event, handler) {
|
|
336
|
+
if (!this.eventHandlers.has(event)) {
|
|
337
|
+
this.eventHandlers.set(event, []);
|
|
338
|
+
}
|
|
339
|
+
this.eventHandlers.get(event).push(handler);
|
|
340
|
+
return this;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
emit(event, data) {
|
|
344
|
+
if (this.eventHandlers.has(event)) {
|
|
345
|
+
this.eventHandlers.get(event).forEach(handler => {
|
|
346
|
+
try {
|
|
347
|
+
handler(data);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
console.error(`❌ Event Handler Fehler '${event}':`, error);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ===== STATUS & STATISTICS =====
|
|
356
|
+
|
|
357
|
+
getStatus() {
|
|
358
|
+
const devices = Array.from(this.devices.entries()).map(([id, device]) => ({
|
|
359
|
+
id,
|
|
360
|
+
status: device.status,
|
|
361
|
+
messageCount: device.messageCount,
|
|
362
|
+
errors: device.errors,
|
|
363
|
+
lastUsed: device.lastUsed
|
|
364
|
+
}));
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
totalDevices: this.devices.size,
|
|
368
|
+
activeDevices: this.activeDevices.size,
|
|
369
|
+
loadBalancing: this.config.loadBalancing,
|
|
370
|
+
devices
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
getHealthCheck() {
|
|
375
|
+
const status = this.getStatus();
|
|
376
|
+
const healthyDevices = status.devices.filter(d => d.status === 'connected').length;
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
healthy: healthyDevices > 0,
|
|
380
|
+
healthyDevices,
|
|
381
|
+
totalDevices: status.totalDevices,
|
|
382
|
+
healthPercentage: Math.round((healthyDevices / status.totalDevices) * 100),
|
|
383
|
+
recommendation: healthyDevices === 0 ? 'Alle Devices reconnecten' :
|
|
384
|
+
healthyDevices < status.totalDevices ? 'Einige Devices prüfen' : 'Alles OK'
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ===== UTILITY =====
|
|
389
|
+
|
|
390
|
+
ensureAuthDir(authDir) {
|
|
391
|
+
if (!fs.existsSync(authDir)) {
|
|
392
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
393
|
+
console.log(`📁 Auth-Directory erstellt: ${authDir}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async cleanup() {
|
|
398
|
+
console.log("🧹 DeviceManager Cleanup...");
|
|
399
|
+
await this.disconnectAll();
|
|
400
|
+
this.devices.clear();
|
|
401
|
+
this.activeDevices.clear();
|
|
402
|
+
this.eventHandlers.clear();
|
|
403
|
+
}
|
|
404
|
+
}
|