waengine 2.4.6 → 2.5.0

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/README.md CHANGED
@@ -1,4 +1,84 @@
1
- # 🚀 WAEngine v1.7.6 - Analytics Bugfix Edition
1
+ # 🚀 WAEngine v2.5.0 - Ultra-Robust Internet Retry-Logig and new message types
2
+
3
+ ---
4
+
5
+ ## 🆕 What's new in v2.5.0
6
+
7
+ ### 🔧 Fixed
8
+ - **Connection recovery** — bot now retries every 5s indefinitely until internet is restored (no more "Recovery already in progress" deadlock)
9
+
10
+ ### ➕ Added — Send Methods
11
+ - **`msg.sendPTV(path)`** — send round video bubble (PTV)
12
+ ```js
13
+ await msg.sendPTV('./video.mp4');
14
+ ```
15
+ - **`msg.sendAll(text)`** — native @everyone mention in groups
16
+ ```js
17
+ await msg.sendAll('Hey everyone!');
18
+ ```
19
+ - **`msg.sendViewOnce(path, type)`** — view-once image, video or audio
20
+ ```js
21
+ await msg.sendViewOnce('./img.jpg', 'image');
22
+ await msg.sendViewOnce('./clip.mp4', 'video');
23
+ await msg.sendViewOnce('./voice.ogg', 'audio');
24
+ ```
25
+ - **`msg.remReact()`** — remove your reaction from a message
26
+ ```js
27
+ await msg.remReact();
28
+ ```
29
+
30
+ ### ➕ Added — Message Properties
31
+ - **`msg.isReply`** / **`msg.quotedMessage`** — detect & read replied messages
32
+ ```js
33
+ if (msg.isReply) console.log(msg.quotedMessage.text);
34
+ ```
35
+ - **`msg.isForwarded`** / **`msg.forwardingScore`** — detect forwarded messages
36
+ ```js
37
+ if (msg.isForwarded) console.log('Forwarded', msg.forwardingScore, 'times');
38
+ ```
39
+ - **`msg.isMedia`** / **`msg.mediaType`** — quick media type check
40
+ ```js
41
+ if (msg.isMedia) console.log(msg.mediaType); // 'image' | 'video' | 'audio' | ...
42
+ ```
43
+ - **`msg.isEveryoneMention`** — detect incoming @everyone
44
+ ```js
45
+ if (msg.isEveryoneMention) await msg.reply('Everyone was mentioned!');
46
+ ```
47
+
48
+ ### ➕ Added — Events
49
+ - **`reaction`** — someone reacted to a message
50
+ ```js
51
+ bot.on('reaction', (r) => console.log(r.from, r.emoji, r.removed));
52
+ ```
53
+ - **`call`** — incoming call detected
54
+ ```js
55
+ bot.on('call', (call) => console.log(call.from, call.isVideo, call.status));
56
+ ```
57
+ - **`typing`** — someone is typing
58
+ ```js
59
+ bot.on('typing', ({ from, isTyping }) => console.log(from, isTyping));
60
+ ```
61
+ - **`recording`** — someone is recording audio
62
+ ```js
63
+ bot.on('recording', ({ from }) => console.log(from + ' is recording...'));
64
+ ```
65
+ - **`message.delete`** — a message was deleted
66
+ ```js
67
+ bot.on('message.delete', (item) => console.log('deleted:', item));
68
+ ```
69
+ - **`group.update`** — group name/description changed
70
+ ```js
71
+ bot.on('group.update', (u) => console.log(u.subject, u.desc));
72
+ ```
73
+ - **`labels.association`** / **`labels.edit`** — WhatsApp Business label events
74
+ ```js
75
+ bot.on('labels.association', (a) => console.log(a));
76
+ bot.on('labels.edit', (l) => console.log(l));
77
+ ```
78
+
79
+ ---
80
+
81
+
2
82
 
3
83
  [![NPM Version](https://img.shields.io/npm/v/waengine)](https://www.npmjs.com/package/waengine)
4
84
  [![Downloads](https://img.shields.io/npm/dm/waengine)](https://www.npmjs.com/package/waengine)
@@ -1569,6 +1649,6 @@ console.log('Most Common:', stats.mostCommonError);
1569
1649
  console.log('By Code:', stats.errorsByCode);
1570
1650
  ```
1571
1651
 
1572
- **📖 Full Documentation:** [ERROR-CODES.md](./ERROR-CODES.md)
1652
+ **📖 Full Documentation:** [FEATURES.md](./FEATURES.md)
1573
1653
 
1574
1654
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waengine",
3
- "version": "2.4.6",
3
+ "version": "2.5.0",
4
4
  "description": "🚀 WAEngine - The most powerful WhatsApp Bot Library with 860+ Working Features, Complete Baileys Integration & Production-Ready Stability",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -0,0 +1,347 @@
1
+ // ===== BOX CONSOLE - Schöne detaillierte Message Boxes =====
2
+
3
+ export class BoxConsole {
4
+ constructor(client) {
5
+ this.client = client;
6
+ this.enabled = false;
7
+
8
+ // Speichere originale Console-Funktionen
9
+ this.originalLog = console.log.bind(console);
10
+ this.originalWarn = console.warn.bind(console);
11
+ this.originalError = console.error.bind(console);
12
+
13
+ // Flag ob Console bereits gepatcht wurde
14
+ this.isPatched = false;
15
+ }
16
+
17
+ // Aktiviere/Deaktiviere Box Console
18
+ enable(enabled = true) {
19
+ this.enabled = enabled;
20
+
21
+ if (enabled && !this.isPatched) {
22
+ // Patche Console-Funktionen
23
+ this.patchConsole();
24
+ this.originalLog('\n✅ Box Console aktiviert - Normale Message-Logs deaktiviert\n');
25
+ } else if (!enabled && this.isPatched) {
26
+ // Stelle originale Console wieder her
27
+ this.unpatchConsole();
28
+ this.originalLog('\n❌ Box Console deaktiviert - Normale Logs reaktiviert\n');
29
+ }
30
+ }
31
+
32
+ patchConsole() {
33
+ const self = this;
34
+
35
+ // Patche console.warn
36
+ console.warn = function(...args) {
37
+ if (self.enabled) {
38
+ // Zeige Warning in Box
39
+ const warningText = args.join(' ');
40
+ self.showWarningBox(warningText);
41
+ } else {
42
+ self.originalWarn(...args);
43
+ }
44
+ };
45
+
46
+ // Patche console.error
47
+ console.error = function(...args) {
48
+ if (self.enabled) {
49
+ // Zeige Error in Box
50
+ const errorText = args.join(' ');
51
+ const error = args[0] instanceof Error ? args[0] : new Error(errorText);
52
+ self.showErrorBox(error);
53
+ } else {
54
+ self.originalError(...args);
55
+ }
56
+ };
57
+
58
+ this.isPatched = true;
59
+ }
60
+
61
+ unpatchConsole() {
62
+ // Stelle originale Funktionen wieder her
63
+ console.warn = this.originalWarn;
64
+ console.error = this.originalError;
65
+
66
+ this.isPatched = false;
67
+ }
68
+
69
+ // Zeige Message Box
70
+ showMessageBox(msg) {
71
+ if (!this.enabled) return;
72
+
73
+ const box = this.createMessageBox(msg);
74
+ this.originalLog(box);
75
+ }
76
+
77
+ // Erstelle Message Box
78
+ createMessageBox(msg) {
79
+ // Extrahiere alle Infos
80
+ const name = msg.pushName || msg.verifiedBizName || 'Unbekannt';
81
+ const text = msg.body || msg.text || msg.message?.conversation || '[Keine Text-Nachricht]';
82
+ const device = this.detectDevice(msg);
83
+ const jid = msg.from || msg.key?.remoteJid || 'N/A';
84
+ const lid = msg.key?.participant?.split('@')[0] || jid.split('@')[0] || 'N/A';
85
+ const phone = this.extractPhoneNumber(jid);
86
+ const isCommand = this.isCommand(text);
87
+ const chatType = this.getChatType(jid);
88
+ const messageType = this.getMessageType(msg);
89
+ const userRole = this.getUserRole(msg, jid);
90
+
91
+ // Erstelle Box
92
+ const width = 70;
93
+ const line = '═'.repeat(width);
94
+
95
+ let box = `\n╔${line}╗\n`;
96
+ box += this.boxLine('📨 NEUE NACHRICHT', width, 'center');
97
+ box += `╠${line}╣\n`;
98
+
99
+ // User Info
100
+ box += this.boxLine(`👤 Name: ${name}`, width);
101
+ box += this.boxLine(`📱 Telefon: ${phone}`, width);
102
+ box += this.boxLine(`🆔 JID: ${jid}`, width);
103
+ box += this.boxLine(`🔢 LID: ${lid}`, width);
104
+
105
+ box += `╠${line}╣\n`;
106
+
107
+ // Message Info
108
+ box += this.boxLine(`💬 Nachricht: ${this.truncate(text, 50)}`, width);
109
+ box += this.boxLine(`📝 Typ: ${messageType}`, width);
110
+ box += this.boxLine(`💻 Gerät: ${device}`, width);
111
+ box += this.boxLine(`⚡ Command: ${isCommand ? 'Ja ✅' : 'Nein ❌'}`, width);
112
+
113
+ box += `╠${line}╣\n`;
114
+
115
+ // Chat Info
116
+ box += this.boxLine(`💭 Chat-Typ: ${chatType}`, width);
117
+ if (chatType === 'Gruppe') {
118
+ box += this.boxLine(`👑 Rolle: ${userRole}`, width);
119
+ }
120
+
121
+ box += `╚${line}╝\n`;
122
+
123
+ return box;
124
+ }
125
+
126
+ // Zeige Error Box
127
+ showErrorBox(error, context = '') {
128
+ const box = this.createErrorBox(error, context);
129
+ this.originalError(box);
130
+ }
131
+
132
+ createErrorBox(error, context) {
133
+ const width = 70;
134
+ const line = '═'.repeat(width);
135
+
136
+ let box = `\n╔${line}╗\n`;
137
+ box += this.boxLine('❌ FEHLER', width, 'center');
138
+ box += `╠${line}╣\n`;
139
+
140
+ const errorMsg = error.message || String(error);
141
+ box += this.boxLine(`🔴 Fehler: ${this.truncate(errorMsg, 60)}`, width);
142
+
143
+ if (context) {
144
+ box += this.boxLine(`📍 Kontext: ${this.truncate(context, 60)}`, width);
145
+ }
146
+
147
+ if (error.stack) {
148
+ const stackLines = error.stack.split('\n').slice(0, 3);
149
+ box += `╠${line}╣\n`;
150
+ box += this.boxLine('📚 Stack Trace:', width);
151
+ stackLines.forEach(line => {
152
+ box += this.boxLine(` ${this.truncate(line.trim(), 65)}`, width);
153
+ });
154
+ }
155
+
156
+ box += `╚${line}╝\n`;
157
+
158
+ return box;
159
+ }
160
+
161
+ // Zeige Warning Box
162
+ showWarningBox(warning, context = '') {
163
+ const box = this.createWarningBox(warning, context);
164
+ this.originalWarn(box);
165
+ }
166
+
167
+ createWarningBox(warning, context) {
168
+ const width = 70;
169
+ const line = '═'.repeat(width);
170
+
171
+ let box = `\n╔${line}╗\n`;
172
+ box += this.boxLine('⚠️ WARNUNG', width, 'center');
173
+ box += `╠${line}╣\n`;
174
+
175
+ box += this.boxLine(`🟡 Warnung: ${this.truncate(String(warning), 60)}`, width);
176
+
177
+ if (context) {
178
+ box += this.boxLine(`📍 Kontext: ${this.truncate(context, 60)}`, width);
179
+ }
180
+
181
+ box += `╚${line}╝\n`;
182
+
183
+ return box;
184
+ }
185
+
186
+ // Helper: Box Line
187
+ boxLine(text, width, align = 'left') {
188
+ const maxTextWidth = width - 4;
189
+ const truncated = this.truncate(text, maxTextWidth);
190
+
191
+ let line = '║ ';
192
+
193
+ if (align === 'center') {
194
+ const padding = Math.floor((maxTextWidth - truncated.length) / 2);
195
+ line += ' '.repeat(padding) + truncated + ' '.repeat(maxTextWidth - truncated.length - padding);
196
+ } else {
197
+ line += truncated + ' '.repeat(maxTextWidth - truncated.length);
198
+ }
199
+
200
+ line += ' ║\n';
201
+ return line;
202
+ }
203
+
204
+ // Helper: Truncate Text
205
+ truncate(text, maxLength) {
206
+ if (!text) return '';
207
+ text = String(text);
208
+ if (text.length <= maxLength) return text;
209
+ return text.substring(0, maxLength - 3) + '...';
210
+ }
211
+
212
+ // Detect Device
213
+ detectDevice(msg) {
214
+ // Nutze device-detector falls verfügbar
215
+ try {
216
+ const messageId = msg.key?.id || msg.id;
217
+ if (messageId) {
218
+ const { DeviceDetector } = require('./device-detector.js');
219
+ const detector = new DeviceDetector();
220
+ const detection = detector.detectDevice(messageId);
221
+
222
+ const deviceNames = {
223
+ 'web': '🌐 WhatsApp Web',
224
+ 'iphone': '📱 iPhone',
225
+ 'android': '🤖 Android',
226
+ 'desktop': '💻 Desktop',
227
+ 'business': '💼 Business',
228
+ 'unknown': '❓ Unbekannt'
229
+ };
230
+
231
+ return deviceNames[detection.deviceType] || '❓ Unbekannt';
232
+ }
233
+ } catch (error) {
234
+ // Fallback
235
+ }
236
+
237
+ return '❓ Unbekannt';
238
+ }
239
+
240
+ // Extract Phone Number
241
+ extractPhoneNumber(jid) {
242
+ if (!jid) return 'N/A';
243
+
244
+ // Format: 491234567890@s.whatsapp.net
245
+ const match = jid.match(/^(\d+)@/);
246
+ if (match) {
247
+ const number = match[1];
248
+ // Formatiere: +49 123 4567890
249
+ if (number.length > 10) {
250
+ return `+${number.substring(0, 2)} ${number.substring(2, 5)} ${number.substring(5)}`;
251
+ }
252
+ return `+${number}`;
253
+ }
254
+
255
+ return jid.split('@')[0] || 'N/A';
256
+ }
257
+
258
+ // Check if Command
259
+ isCommand(text) {
260
+ if (!text) return false;
261
+
262
+ // Prüfe ob Text mit Prefix startet
263
+ const prefix = this.client.prefix || '!';
264
+ return text.trim().startsWith(prefix);
265
+ }
266
+
267
+ // Get Chat Type
268
+ getChatType(jid) {
269
+ if (!jid) return 'Unbekannt';
270
+
271
+ if (jid.endsWith('@g.us')) return 'Gruppe';
272
+ if (jid.endsWith('@s.whatsapp.net')) return 'Privat';
273
+ if (jid.endsWith('@broadcast')) return 'Broadcast';
274
+ if (jid === 'status@broadcast') return 'Status';
275
+
276
+ return 'Unbekannt';
277
+ }
278
+
279
+ // Get Message Type
280
+ getMessageType(msg) {
281
+ const message = msg.message;
282
+ if (!message) return 'Text';
283
+
284
+ if (message.conversation) return 'Text';
285
+ if (message.extendedTextMessage) return 'Text (Extended)';
286
+ if (message.imageMessage) return 'Bild �️';
287
+ if (message.videoMessage) return 'Video 🎥';
288
+ if (message.audioMessage) return 'Audio 🎵';
289
+ if (message.documentMessage) return 'Dokument �';
290
+ if (message.stickerMessage) return 'Sticker 😀';
291
+ if (message.locationMessage) return 'Standort 📍';
292
+ if (message.contactMessage) return 'Kontakt 👤';
293
+ if (message.pollCreationMessage) return 'Umfrage 📊';
294
+ if (message.reactionMessage) return 'Reaktion ❤️';
295
+ if (message.viewOnceMessage) return 'View Once 👁️';
296
+
297
+ return 'Text';
298
+ }
299
+
300
+ // Get User Role (in Groups)
301
+ async getUserRole(msg, jid) {
302
+ // Nur für Gruppen
303
+ if (!jid || !jid.endsWith('@g.us')) {
304
+ return 'N/A';
305
+ }
306
+
307
+ try {
308
+ const participant = msg.key?.participant;
309
+ if (!participant || !this.client.socket) {
310
+ return 'Mitglied �';
311
+ }
312
+
313
+ // Hole Gruppen-Metadaten
314
+ const groupMetadata = await this.client.socket.groupMetadata(jid);
315
+
316
+ if (!groupMetadata || !groupMetadata.participants) {
317
+ return 'Mitglied �';
318
+ }
319
+
320
+ // Finde Participant
321
+ const participantData = groupMetadata.participants.find(p => p.id === participant);
322
+
323
+ if (!participantData) {
324
+ return 'Mitglied 👤';
325
+ }
326
+
327
+ // Prüfe Rolle
328
+ if (participantData.admin === 'superadmin') {
329
+ return 'Superadmin 👑 (Ersteller)';
330
+ }
331
+
332
+ if (participantData.admin === 'admin') {
333
+ return 'Admin 🛡️';
334
+ }
335
+
336
+ return 'Mitglied 👤';
337
+
338
+ } catch (error) {
339
+ return 'Mitglied 👤';
340
+ }
341
+ }
342
+ }
343
+
344
+ // Export für einfache Nutzung
345
+ export function createBoxConsole(client) {
346
+ return new BoxConsole(client);
347
+ }
package/src/client.js CHANGED
@@ -152,12 +152,6 @@ export class WhatsAppClient {
152
152
  // FromMe Ignore System - NEUE GLOBALE API!
153
153
  this.ignoreFromMe = true; // Standard: Bot ignoriert sich selbst
154
154
 
155
- // Log wenn automatisch aktiviert
156
- if (this.ignoreOfflineMessages) {
157
- console.log('\n📵 Offline Message Ignore: AUTOMATISCH AKTIVIERT');
158
- console.log(' └─ Grund: Session über Session Menu gestartet\n');
159
- }
160
-
161
155
  // ===== GLOBAL VISUAL TYPING/RECORDING SYSTEM - NEU! =====
162
156
  this.globalVisualTyping = false; // Global aktiviert/deaktiviert
163
157
  this.globalVisualRecord = false; // Global aktiviert/deaktiviert
@@ -168,11 +162,8 @@ export class WhatsAppClient {
168
162
  this.load = {
169
163
  Plugins: async (pluginName) => {
170
164
  if (pluginName === 'all') {
171
- console.log('🔌 Lade alle Plugins...');
172
165
  await this.plugins.loadAllPlugins();
173
- console.log('✅ Alle Plugins geladen!');
174
166
  } else {
175
- console.log(`🔌 Lade Plugin: ${pluginName}`);
176
167
  await this.plugins.load(pluginName);
177
168
  }
178
169
  }
@@ -183,12 +174,10 @@ export class WhatsAppClient {
183
174
  message: {
184
175
  offline: (enabled = true) => {
185
176
  this.ignoreOfflineMessages = enabled;
186
- console.log(`📵 Offline Message Ignore: ${enabled ? 'AKTIVIERT' : 'DEAKTIVIERT'}`);
187
177
  return this;
188
178
  },
189
179
  FromMe: (enabled = true) => {
190
180
  this.ignoreFromMe = enabled;
191
- console.log(`🤖 FromMe Ignore: ${enabled ? 'AKTIVIERT (Bot ignoriert sich selbst)' : 'DEAKTIVIERT (Bot reagiert auf eigene Messages)'}`);
192
181
  return this;
193
182
  }
194
183
  }
@@ -198,7 +187,6 @@ export class WhatsAppClient {
198
187
  this.info = {
199
188
  Console: (enabled = true) => {
200
189
  this.consoleVerbose = enabled;
201
- console.log(`📊 Console Info Mode: ${enabled ? 'VERBOSE (Alle Details)' : 'NORMAL (Nur wichtige Infos)'}`);
202
190
  return this;
203
191
  }
204
192
  };
@@ -206,7 +194,6 @@ export class WhatsAppClient {
206
194
  this.silent = {
207
195
  Console: (enabled = true) => {
208
196
  this.consoleSilent = enabled;
209
- console.log(`🔇 Console Silent Mode: ${enabled ? 'AKTIVIERT (Nur gesendete Messages)' : 'DEAKTIVIERT (Normal)'}`);
210
197
  return this;
211
198
  }
212
199
  };
@@ -216,12 +203,10 @@ export class WhatsAppClient {
216
203
  this.set = {
217
204
  Visualtyping: (ms) => {
218
205
  this.visualTypingDuration = ms;
219
- console.log(`⌨️ Visual Typing Dauer: ${ms}ms`);
220
206
  return this;
221
207
  },
222
208
  Visualrecord: (ms) => {
223
209
  this.visualRecordDuration = ms;
224
- console.log(`🎤 Visual Record Dauer: ${ms}ms`);
225
210
  return this;
226
211
  }
227
212
  };
@@ -1156,6 +1141,32 @@ export class WhatsAppClient {
1156
1141
 
1157
1142
  this.socket.ev.on("presence.update", (update) => {
1158
1143
  this.emit('presence.update', update);
1144
+
1145
+ // Tippen / Aufnehmen erkennen - sauber extrahiert
1146
+ if (update.presences) {
1147
+ Object.entries(update.presences).forEach(([jid, presence]) => {
1148
+ if (presence.lastKnownPresence === 'composing') {
1149
+ this.emit('typing', {
1150
+ from: jid,
1151
+ chat: update.id,
1152
+ isTyping: true
1153
+ });
1154
+ } else if (presence.lastKnownPresence === 'recording') {
1155
+ this.emit('recording', {
1156
+ from: jid,
1157
+ chat: update.id,
1158
+ isRecording: true
1159
+ });
1160
+ } else if (presence.lastKnownPresence === 'paused') {
1161
+ // Hat aufgehört zu tippen
1162
+ this.emit('typing', {
1163
+ from: jid,
1164
+ chat: update.id,
1165
+ isTyping: false
1166
+ });
1167
+ }
1168
+ });
1169
+ }
1159
1170
  });
1160
1171
 
1161
1172
  this.socket.ev.on("group-participants.update", (update) => {
@@ -1169,6 +1180,67 @@ export class WhatsAppClient {
1169
1180
  this.socket.ev.on("contacts.upsert", (contacts) => {
1170
1181
  this.emit('contacts.upsert', contacts);
1171
1182
  });
1183
+
1184
+ // Reaktionen auf Nachrichten erkennen
1185
+ this.socket.ev.on("messages.upsert", ({ messages }) => {
1186
+ messages.forEach(msg => {
1187
+ if (msg.message?.reactionMessage) {
1188
+ const reaction = msg.message.reactionMessage;
1189
+ this.emit('reaction', {
1190
+ from: msg.key.participant || msg.key.remoteJid,
1191
+ chat: msg.key.remoteJid,
1192
+ messageId: reaction.key?.id,
1193
+ emoji: reaction.text, // leer = Reaktion entfernt
1194
+ removed: reaction.text === '',
1195
+ timestamp: msg.messageTimestamp,
1196
+ raw: msg
1197
+ });
1198
+ }
1199
+ });
1200
+ });
1201
+
1202
+ // Eingehende Anrufe erkennen
1203
+ this.socket.ev.on("call", (calls) => {
1204
+ calls.forEach(call => {
1205
+ this.emit('call', {
1206
+ id: call.id,
1207
+ from: call.chatId,
1208
+ isVideo: call.isVideo,
1209
+ isGroup: call.isGroup,
1210
+ status: call.status, // 'offer' | 'ringing' | 'accept' | 'timeout' | 'reject'
1211
+ timestamp: call.date,
1212
+ raw: call
1213
+ });
1214
+ });
1215
+ });
1216
+
1217
+ // Nachrichten gelöscht erkennen
1218
+ this.socket.ev.on("messages.delete", (item) => {
1219
+ this.emit('message.delete', item);
1220
+ });
1221
+
1222
+ // Gruppen-Info geändert (Name, Beschreibung, Bild)
1223
+ this.socket.ev.on("groups.update", (updates) => {
1224
+ updates.forEach(update => {
1225
+ this.emit('group.update', {
1226
+ id: update.id,
1227
+ subject: update.subject, // neuer Gruppenname
1228
+ desc: update.desc, // neue Beschreibung
1229
+ announce: update.announce, // nur Admins können schreiben
1230
+ restrict: update.restrict,
1231
+ raw: update
1232
+ });
1233
+ });
1234
+ });
1235
+
1236
+ // WhatsApp Business Labels
1237
+ this.socket.ev.on("labels.association", (association) => {
1238
+ this.emit('labels.association', association);
1239
+ });
1240
+
1241
+ this.socket.ev.on("labels.edit", (label) => {
1242
+ this.emit('labels.edit', label);
1243
+ });
1172
1244
  }
1173
1245
 
1174
1246
  parseMessage(msg) {
@@ -1193,15 +1265,57 @@ export class WhatsAppClient {
1193
1265
  text = msg.message.templateButtonReplyMessage.selectedId;
1194
1266
  }
1195
1267
 
1268
+ // @everyone / @all Erkennung (neue Baileys-Funktion)
1269
+ const mentionedJids = msg.message?.extendedTextMessage?.contextInfo?.mentionedJid || [];
1270
+ const isEveryoneMention = mentionedJids.includes('0@s.whatsapp.net') ||
1271
+ msg.message?.extendedTextMessage?.contextInfo?.mentionedJid?.some(j => j === '0@s.whatsapp.net') ||
1272
+ text?.includes('@everyone') ||
1273
+ text?.includes('@all');
1274
+
1275
+ // Reply / Zitat erkennen
1276
+ const contextInfo = msg.message?.extendedTextMessage?.contextInfo ||
1277
+ msg.message?.imageMessage?.contextInfo ||
1278
+ msg.message?.videoMessage?.contextInfo ||
1279
+ msg.message?.audioMessage?.contextInfo ||
1280
+ msg.message?.documentMessage?.contextInfo ||
1281
+ msg.message?.stickerMessage?.contextInfo || null;
1282
+
1283
+ const isReply = !!(contextInfo?.quotedMessage);
1284
+ const quotedMessage = isReply ? {
1285
+ id: contextInfo.stanzaId,
1286
+ from: contextInfo.participant || contextInfo.remoteJid,
1287
+ message: contextInfo.quotedMessage,
1288
+ text: contextInfo.quotedMessage?.conversation ||
1289
+ contextInfo.quotedMessage?.extendedTextMessage?.text ||
1290
+ contextInfo.quotedMessage?.imageMessage?.caption ||
1291
+ contextInfo.quotedMessage?.videoMessage?.caption || null
1292
+ } : null;
1293
+
1294
+ // Weitergeleitet erkennen
1295
+ const isForwarded = !!(contextInfo?.isForwarded);
1296
+ const forwardingScore = contextInfo?.forwardingScore || 0;
1297
+
1298
+ // Medientyp
1299
+ const mediaTypes = ['image', 'video', 'audio', 'document', 'sticker'];
1300
+ const msgType = this.getMessageType(msg.message);
1301
+ const isMedia = mediaTypes.includes(msgType);
1302
+
1196
1303
  return {
1197
1304
  id: msg.key.id,
1198
1305
  from: msg.key.remoteJid,
1199
1306
  fromMe: msg.key.fromMe,
1200
1307
  text: text,
1201
1308
  timestamp: msg.messageTimestamp,
1202
- type: this.getMessageType(msg.message),
1309
+ type: msgType,
1203
1310
  isGroup: msg.key.remoteJid?.includes("@g.us"),
1204
- participant: msg.key.participant, // Wichtig für Gruppen
1311
+ participant: msg.key.participant,
1312
+ isEveryoneMention,
1313
+ isReply,
1314
+ quotedMessage,
1315
+ isForwarded,
1316
+ forwardingScore,
1317
+ isMedia,
1318
+ mediaType: isMedia ? msgType : null,
1205
1319
  raw: msg
1206
1320
  };
1207
1321
  }
@@ -128,12 +128,20 @@ export class ConnectionRecovery {
128
128
 
129
129
  console.log('🛡️ Initiating connection recovery...');
130
130
 
131
- try {
132
- await this.executeRecoveryStrategies();
133
- } catch (recoveryError) {
134
- console.error('❌ Recovery failed:', recoveryError.message);
135
- } finally {
136
- this.state.isRecovering = false;
131
+ // Endlose Retry-Schleife: läuft so lange bis Verbindung wieder steht
132
+ while (this.state.isRecovering) {
133
+ try {
134
+ await this.executeRecoveryStrategies();
135
+ // Erfolgreich verbunden
136
+ this.state.isRecovering = false;
137
+ return;
138
+ } catch (recoveryError) {
139
+ console.log(`❌ Recovery attempt failed: ${recoveryError.message}`);
140
+ console.log('🌐 Waiting for internet... retrying in 5s');
141
+ // Retrycount zurücksetzen damit nächste Runde wieder alle Strategien versucht
142
+ this.state.retryCount = 0;
143
+ await new Promise(resolve => setTimeout(resolve, 5000));
144
+ }
137
145
  }
138
146
  }
139
147
 
@@ -147,10 +155,6 @@ export class ConnectionRecovery {
147
155
  ];
148
156
 
149
157
  for (const strategy of strategies) {
150
- if (this.state.retryCount >= this.options.maxRetries) {
151
- throw new Error('Max retries exceeded');
152
- }
153
-
154
158
  try {
155
159
  console.log(`🔧 Trying strategy: ${strategy.name}`);
156
160
  await strategy.fn();
@@ -281,7 +285,7 @@ export class ConnectionRecovery {
281
285
  console.log('🔄 Recovery state reset');
282
286
  }
283
287
 
284
- // Cleanup
288
+ // Cleanup - setzt isRecovering auf false, damit die Retry-Schleife stoppt
285
289
  cleanup() {
286
290
  this.stopHealthMonitoring();
287
291
  this.state.isRecovering = false;
@@ -1,5 +1,8 @@
1
1
  // ===== CONSOLE RENDERER - Schöne Console Outputs mit Prefix & Farben =====
2
2
 
3
+ // Prüfe ob wir im Session Menu sind (kein Prefix) oder im Bot-Terminal (mit Prefix)
4
+ const isSessionMenu = !process.env.__WAENGINE_SKIP_SESSION_MENU__;
5
+
3
6
  // ANSI Color Codes
4
7
  const colors = {
5
8
  // Basic Colors
@@ -89,6 +92,11 @@ console.log = function(...args) {
89
92
  return originalConsole.log(...args);
90
93
  }
91
94
 
95
+ // WICHTIG: Kein Prefix im Session Menu!
96
+ if (isSessionMenu) {
97
+ return originalConsole.log(...args);
98
+ }
99
+
92
100
  const prefix = formatPrefix(globalConfig.prefix, globalConfig.prefixColor);
93
101
  const colorCode = getColor(globalConfig.defaultColor);
94
102
 
@@ -105,6 +113,11 @@ console.error = function(...args) {
105
113
  return originalConsole.error(...args);
106
114
  }
107
115
 
116
+ // WICHTIG: Kein Prefix im Session Menu!
117
+ if (isSessionMenu) {
118
+ return originalConsole.error(...args);
119
+ }
120
+
108
121
  const prefix = formatPrefix(globalConfig.prefix, globalConfig.prefixColor);
109
122
  const colorCode = getColor(globalConfig.errorColor);
110
123
 
@@ -121,6 +134,11 @@ console.warn = function(...args) {
121
134
  return originalConsole.warn(...args);
122
135
  }
123
136
 
137
+ // WICHTIG: Kein Prefix im Session Menu!
138
+ if (isSessionMenu) {
139
+ return originalConsole.warn(...args);
140
+ }
141
+
124
142
  const prefix = formatPrefix(globalConfig.prefix, globalConfig.prefixColor);
125
143
  const colorCode = getColor(globalConfig.warnColor);
126
144
 
@@ -137,6 +155,11 @@ console.info = function(...args) {
137
155
  return originalConsole.info(...args);
138
156
  }
139
157
 
158
+ // WICHTIG: Kein Prefix im Session Menu!
159
+ if (isSessionMenu) {
160
+ return originalConsole.info(...args);
161
+ }
162
+
140
163
  const prefix = formatPrefix(globalConfig.prefix, globalConfig.prefixColor);
141
164
  const colorCode = getColor(globalConfig.infoColor);
142
165
 
@@ -153,68 +176,55 @@ export const render = {
153
176
  // Set prefix name
154
177
  consolePrefix(name) {
155
178
  globalConfig.prefix = name;
156
- console.log(`✅ Console Prefix gesetzt: [${name}]`);
157
179
  },
158
180
 
159
181
  // Set prefix color
160
182
  consolePrefixColor(color) {
161
183
  if (!colors[color]) {
162
- console.warn(`⚠️ Unbekannte Farbe: ${color}`);
163
184
  return;
164
185
  }
165
186
  globalConfig.prefixColor = color;
166
- console.log(`🎨 Prefix Farbe gesetzt: ${color}`);
167
187
  },
168
188
 
169
189
  // Set default console.log color
170
190
  consoleColor(color) {
171
191
  if (!colors[color]) {
172
- console.warn(`⚠️ Unbekannte Farbe: ${color}`);
173
192
  return;
174
193
  }
175
194
  globalConfig.defaultColor = color;
176
- console.log(`🎨 Console Farbe gesetzt: ${color}`);
177
195
  },
178
196
 
179
197
  // Set console.error color
180
198
  consoleError(color) {
181
199
  if (!colors[color]) {
182
- console.warn(`⚠️ Unbekannte Farbe: ${color}`);
183
200
  return;
184
201
  }
185
202
  globalConfig.errorColor = color;
186
- console.log(`🎨 Error Farbe gesetzt: ${color}`);
187
203
  },
188
204
 
189
205
  // Set console.warn color
190
206
  consoleWarn(color) {
191
207
  if (!colors[color]) {
192
- console.warn(`⚠️ Unbekannte Farbe: ${color}`);
193
208
  return;
194
209
  }
195
210
  globalConfig.warnColor = color;
196
- console.log(`🎨 Warn Farbe gesetzt: ${color}`);
197
211
  },
198
212
 
199
213
  // Set console.info color
200
214
  consoleInfo(color) {
201
215
  if (!colors[color]) {
202
- console.warn(`⚠️ Unbekannte Farbe: ${color}`);
203
216
  return;
204
217
  }
205
218
  globalConfig.infoColor = color;
206
- console.log(`🎨 Info Farbe gesetzt: ${color}`);
207
219
  },
208
220
 
209
221
  // Enable/Disable rendering
210
222
  enable() {
211
223
  globalConfig.enabled = true;
212
- console.log('✅ Console Rendering aktiviert');
213
224
  },
214
225
 
215
226
  disable() {
216
227
  globalConfig.enabled = false;
217
- originalConsole.log('⚠️ Console Rendering deaktiviert');
218
228
  },
219
229
 
220
230
  // Reset to defaults
@@ -229,7 +239,6 @@ export const render = {
229
239
  successColor: 'green',
230
240
  enabled: true
231
241
  };
232
- console.log('🔄 Console Rendering zurückgesetzt');
233
242
  },
234
243
 
235
244
  // Get current config
@@ -271,12 +280,3 @@ export const render = {
271
280
 
272
281
  // Export colors for direct use
273
282
  export { colors };
274
-
275
- // Auto-initialize message
276
- if (globalConfig.enabled) {
277
- originalConsole.log(
278
- `${colors.brightCyan}╔════════════════════════════════════════╗${colors.reset}\n` +
279
- `${colors.brightCyan}║ 🎨 Console Renderer aktiviert! ║${colors.reset}\n` +
280
- `${colors.brightCyan}╚════════════════════════════════════════╝${colors.reset}`
281
- );
282
- }
package/src/message.js CHANGED
@@ -13,6 +13,13 @@ export class Message {
13
13
  this.timestamp = data.timestamp;
14
14
  this.type = data.type;
15
15
  this.isGroup = data.isGroup;
16
+ this.isEveryoneMention = data.isEveryoneMention || false;
17
+ this.isReply = data.isReply || false;
18
+ this.quotedMessage = data.quotedMessage || null;
19
+ this.isForwarded = data.isForwarded || false;
20
+ this.forwardingScore = data.forwardingScore || 0;
21
+ this.isMedia = data.isMedia || false;
22
+ this.mediaType = data.mediaType || null;
16
23
  this.raw = data.raw;
17
24
 
18
25
  // Command Properties (werden vom Client gesetzt)
@@ -336,6 +343,38 @@ export class Message {
336
343
  }
337
344
  return results;
338
345
  }
346
+ // PTV = Personal/Private To View - runde Video-Bubble im Chat
347
+ async sendPTV(videoSource, mentions = []) {
348
+ try {
349
+ // Unterstützt: Dateipfad (string), URL (string), Buffer, Base64-String
350
+ let videoData;
351
+ if (Buffer.isBuffer(videoSource)) {
352
+ videoData = videoSource;
353
+ } else if (typeof videoSource === 'string' && videoSource.startsWith('data:')) {
354
+ // Base64 data URL: "data:video/mp4;base64,..."
355
+ const base64 = videoSource.split(',')[1];
356
+ videoData = Buffer.from(base64, 'base64');
357
+ } else {
358
+ // Dateipfad oder URL
359
+ videoData = { url: videoSource };
360
+ }
361
+
362
+ const message = {
363
+ video: videoData,
364
+ ptv: true,
365
+ mimetype: 'video/mp4'
366
+ };
367
+
368
+ if (mentions.length > 0) {
369
+ message.mentions = mentions;
370
+ }
371
+
372
+ return await this.client.socket.sendMessage(this.from, message);
373
+ } catch (error) {
374
+ console.error('❌ Fehler beim Senden des PTV:', error);
375
+ throw error;
376
+ }
377
+ }
339
378
 
340
379
  async sendGif(gifPath, caption = "", mentions = []) {
341
380
  const message = {
@@ -951,6 +990,61 @@ export class Message {
951
990
  return await this.reply(mentionText, allMembers);
952
991
  }
953
992
 
993
+ // @everyone / @all - neue native WhatsApp Funktion (Baileys: mention 0@s.whatsapp.net)
994
+ async sendAll(text) {
995
+ if (!this.isGroup) {
996
+ throw new Error('sendAll funktioniert nur in Gruppen');
997
+ }
998
+
999
+ return await this.client.socket.sendMessage(this.from, {
1000
+ text: text,
1001
+ mentions: ['0@s.whatsapp.net']
1002
+ });
1003
+ }
1004
+
1005
+ // Reaktion entfernen
1006
+ async remReact() {
1007
+ return await this.client.socket.sendMessage(this.from, {
1008
+ react: { text: '', key: this.raw.key }
1009
+ });
1010
+ }
1011
+
1012
+ // View-Once senden (Bild, Video oder Audio das sich nach einmaligem Ansehen löscht)
1013
+ async sendViewOnce(mediaSource, type = 'image', caption = '') {
1014
+ try {
1015
+ let mediaData;
1016
+ if (Buffer.isBuffer(mediaSource)) {
1017
+ mediaData = mediaSource;
1018
+ } else {
1019
+ mediaData = { url: mediaSource };
1020
+ }
1021
+
1022
+ const message = {};
1023
+ if (type === 'image') {
1024
+ message.image = mediaData;
1025
+ message.caption = caption;
1026
+ message.viewOnce = true;
1027
+ } else if (type === 'video') {
1028
+ message.video = mediaData;
1029
+ message.caption = caption;
1030
+ message.viewOnce = true;
1031
+ message.mimetype = 'video/mp4';
1032
+ } else if (type === 'audio') {
1033
+ message.audio = mediaData;
1034
+ message.viewOnce = true;
1035
+ message.mimetype = 'audio/ogg; codecs=opus';
1036
+ message.ptt = true;
1037
+ } else {
1038
+ throw new Error('sendViewOnce: type muss "image", "video" oder "audio" sein');
1039
+ }
1040
+
1041
+ return await this.client.socket.sendMessage(this.from, message);
1042
+ } catch (error) {
1043
+ console.error('❌ Fehler beim Senden der View-Once Nachricht:', error);
1044
+ throw error;
1045
+ }
1046
+ }
1047
+
954
1048
  // Mention mit Typing - Neue Funktion für bessere UX
955
1049
  async slowTypeWithMention(text, userJid) {
956
1050
  const senderName = userJid.split('@')[0];
package/src/storage.js CHANGED
@@ -165,15 +165,45 @@ export class WAStorage {
165
165
  // Schreibe in temporäre Datei
166
166
  fs.writeFileSync(tempFilePath, jsonData);
167
167
 
168
- // Atomare Umbenennung (verhindert korrupte Dateien)
169
- fs.renameSync(tempFilePath, filePath);
168
+ // Atomare Umbenennung mit Retry für Windows EPERM
169
+ let renamed = false;
170
+ let lastError = null;
171
+ const maxRetries = 3;
172
+ const retryDelays = [50, 100, 150]; // ms
173
+
174
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
175
+ try {
176
+ fs.renameSync(tempFilePath, filePath);
177
+ renamed = true;
178
+ break;
179
+ } catch (error) {
180
+ lastError = error;
181
+ if (error.code === 'EPERM' && attempt < maxRetries - 1) {
182
+ // Wait before retry (Windows file lock issue)
183
+ const delay = retryDelays[attempt];
184
+ const start = Date.now();
185
+ while (Date.now() - start < delay) {
186
+ // Busy wait
187
+ }
188
+ } else {
189
+ throw error;
190
+ }
191
+ }
192
+ }
193
+
194
+ if (!renamed) {
195
+ throw lastError || new Error('Rename failed');
196
+ }
170
197
 
171
198
  // Cache mit Größenlimit aktualisieren
172
199
  this.updateCache(fileName, data);
173
200
 
174
201
  return true;
175
202
  } catch (error) {
176
- console.error(`❌ Fehler beim Schreiben in ${fileName}:`, error);
203
+ // Suppress EPERM error logging (too noisy on Windows)
204
+ if (error.code !== 'EPERM') {
205
+ console.error(`❌ Fehler beim Schreiben in ${fileName}:`, error);
206
+ }
177
207
 
178
208
  // Cleanup temporäre Datei
179
209
  try {