waengine 2.4.9 → 2.5.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/FEATURES.md CHANGED
@@ -4832,6 +4832,75 @@ bot.when('check @user online').checkOnline().done(); // Online-Status von Mentio
4832
4832
 
4833
4833
  ---
4834
4834
 
4835
+ ## Sonstiges
4836
+
4837
+ - **`msg.sendPTV(path)`** — send round video bubble (PTV)
4838
+ ```js
4839
+ await msg.sendPTV('./video.mp4');
4840
+ ```
4841
+ - **`msg.sendAll(text)`** — native @everyone mention in groups
4842
+ ```js
4843
+ await msg.sendAll('Hey everyone!');
4844
+ ```
4845
+ - **`msg.sendViewOnce(path, type)`** — view-once image, video or audio
4846
+ ```js
4847
+ await msg.sendViewOnce('./img.jpg', 'image');
4848
+ await msg.sendViewOnce('./clip.mp4', 'video');
4849
+ await msg.sendViewOnce('./voice.ogg', 'audio');
4850
+ ```
4851
+ - **`msg.remReact()`** — remove your reaction from a message
4852
+ ```js
4853
+ await msg.remReact();
4854
+ ```
4855
+
4856
+ - **`msg.isReply`** / **`msg.quotedMessage`** — detect & read replied messages
4857
+ ```js
4858
+ if (msg.isReply) console.log(msg.quotedMessage.text);
4859
+ ```
4860
+ - **`msg.isForwarded`** / **`msg.forwardingScore`** — detect forwarded messages
4861
+ ```js
4862
+ if (msg.isForwarded) console.log('Forwarded', msg.forwardingScore, 'times');
4863
+ ```
4864
+ - **`msg.isMedia`** / **`msg.mediaType`** — quick media type check
4865
+ ```js
4866
+ if (msg.isMedia) console.log(msg.mediaType); // 'image' | 'video' | 'audio' | ...
4867
+ ```
4868
+ - **`msg.isEveryoneMention`** — detect incoming @everyone
4869
+ ```js
4870
+ if (msg.isEveryoneMention) await msg.reply('Everyone was mentioned!');
4871
+ ```
4872
+
4873
+ ### ➕ Added — Events
4874
+ - **`reaction`** — someone reacted to a message
4875
+ ```js
4876
+ bot.on('reaction', (r) => console.log(r.from, r.emoji, r.removed));
4877
+ ```
4878
+ - **`call`** — incoming call detected
4879
+ ```js
4880
+ bot.on('call', (call) => console.log(call.from, call.isVideo, call.status));
4881
+ ```
4882
+ - **`typing`** — someone is typing
4883
+ ```js
4884
+ bot.on('typing', ({ from, isTyping }) => console.log(from, isTyping));
4885
+ ```
4886
+ - **`recording`** — someone is recording audio
4887
+ ```js
4888
+ bot.on('recording', ({ from }) => console.log(from + ' is recording...'));
4889
+ ```
4890
+ - **`message.delete`** — a message was deleted
4891
+ ```js
4892
+ bot.on('message.delete', (item) => console.log('deleted:', item));
4893
+ ```
4894
+ - **`group.update`** — group name/description changed
4895
+ ```js
4896
+ bot.on('group.update', (u) => console.log(u.subject, u.desc));
4897
+ ```
4898
+ - **`labels.association`** / **`labels.edit`** — WhatsApp Business label events
4899
+ ```js
4900
+ bot.on('labels.association', (a) => console.log(a));
4901
+ bot.on('labels.edit', (l) => console.log(l));
4902
+ ```
4903
+
4835
4904
  *Made with ❤️ for WhatsApp Automation*
4836
4905
 
4837
4906
  **Die mächtigste WhatsApp Bot Library - von 3-Zeilen-Bots bis zu KI-gestützten Enterprise Multi-Device Systemen mit vollständigem Plugin-Ecosystem (8 Plugins, 80+ Commands), 800+ Advanced Features in 16 Kategorien, Security Manager, Gaming Manager, Database Manager, A/B Testing, Reporting System, Cross-Platform Integration, UI Components, Hidetag, Sticker Creation, Visual Recording, Rich Content (Buttons, Lists, Carousels), Business Features, Privacy & Security, Analytics & Monitoring und Profile Picture API!**
package/README.md CHANGED
@@ -1,4 +1,11 @@
1
- # 🚀 WAEngine v1.7.6 - Analytics Bugfix Edition
1
+ # 🚀 WAEngine v2.5.1 - Fixed Retry Logic
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)
2
9
 
3
10
  [![NPM Version](https://img.shields.io/npm/v/waengine)](https://www.npmjs.com/package/waengine)
4
11
  [![Downloads](https://img.shields.io/npm/dm/waengine)](https://www.npmjs.com/package/waengine)
@@ -1569,6 +1576,6 @@ console.log('Most Common:', stats.mostCommonError);
1569
1576
  console.log('By Code:', stats.errorsByCode);
1570
1577
  ```
1571
1578
 
1572
- **📖 Full Documentation:** [ERROR-CODES.md](./ERROR-CODES.md)
1579
+ **📖 Full Documentation:** [FEATURES.md](./FEATURES.md)
1573
1580
 
1574
1581
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waengine",
3
- "version": "2.4.9",
3
+ "version": "2.5.1",
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",
package/src/client.js CHANGED
@@ -1141,6 +1141,32 @@ export class WhatsAppClient {
1141
1141
 
1142
1142
  this.socket.ev.on("presence.update", (update) => {
1143
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
+ }
1144
1170
  });
1145
1171
 
1146
1172
  this.socket.ev.on("group-participants.update", (update) => {
@@ -1154,6 +1180,67 @@ export class WhatsAppClient {
1154
1180
  this.socket.ev.on("contacts.upsert", (contacts) => {
1155
1181
  this.emit('contacts.upsert', contacts);
1156
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
+ });
1157
1244
  }
1158
1245
 
1159
1246
  parseMessage(msg) {
@@ -1178,15 +1265,57 @@ export class WhatsAppClient {
1178
1265
  text = msg.message.templateButtonReplyMessage.selectedId;
1179
1266
  }
1180
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
+
1181
1303
  return {
1182
1304
  id: msg.key.id,
1183
1305
  from: msg.key.remoteJid,
1184
1306
  fromMe: msg.key.fromMe,
1185
1307
  text: text,
1186
1308
  timestamp: msg.messageTimestamp,
1187
- type: this.getMessageType(msg.message),
1309
+ type: msgType,
1188
1310
  isGroup: msg.key.remoteJid?.includes("@g.us"),
1189
- 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,
1190
1319
  raw: msg
1191
1320
  };
1192
1321
  }
@@ -107,7 +107,11 @@ export class ConnectionRecovery {
107
107
  }
108
108
 
109
109
  console.log('⚠️ Health check failed:', error.message);
110
- await this.initiateRecovery(error);
110
+ // Fire-and-forget: Recovery läuft unabhängig vom Health-Check-Timer
111
+ this.initiateRecovery(error).catch(e => {
112
+ console.log('🔴 Recovery loop crashed, resetting state:', e?.message || e);
113
+ this.state.isRecovering = false;
114
+ });
111
115
  }
112
116
  }
113
117
 
@@ -121,19 +125,33 @@ export class ConnectionRecovery {
121
125
  this.state.isRecovering = true;
122
126
  this.state.lastError = error;
123
127
  this.state.failureHistory.push({
124
- error: error.message,
128
+ error: error?.message || String(error),
125
129
  timestamp: Date.now(),
126
130
  retryCount: this.state.retryCount
127
131
  });
128
132
 
129
133
  console.log('🛡️ Initiating connection recovery...');
130
134
 
131
- try {
132
- await this.executeRecoveryStrategies();
133
- } catch (recoveryError) {
134
- console.error('❌ Recovery failed:', recoveryError.message);
135
- } finally {
136
- this.state.isRecovering = false;
135
+ let attempt = 0;
136
+
137
+ // Endlose Retry-Schleife: läuft so lange bis Verbindung wieder steht
138
+ while (this.state.isRecovering) {
139
+ attempt++;
140
+ console.log(`🔁 Recovery attempt #${attempt}...`);
141
+ try {
142
+ await this.executeRecoveryStrategies();
143
+ // Erfolgreich verbunden
144
+ this.state.isRecovering = false;
145
+ console.log(`✅ Connection restored after ${attempt} attempt(s)`);
146
+ return;
147
+ } catch (recoveryError) {
148
+ const msg = recoveryError?.message || String(recoveryError);
149
+ console.log(`❌ Recovery attempt #${attempt} failed: ${msg}`);
150
+ console.log('🌐 Waiting for internet... retrying in 5s');
151
+ // Retrycount zurücksetzen damit nächste Runde wieder alle Strategien versucht
152
+ this.state.retryCount = 0;
153
+ await new Promise(resolve => setTimeout(resolve, 5000));
154
+ }
137
155
  }
138
156
  }
139
157
 
@@ -147,10 +165,6 @@ export class ConnectionRecovery {
147
165
  ];
148
166
 
149
167
  for (const strategy of strategies) {
150
- if (this.state.retryCount >= this.options.maxRetries) {
151
- throw new Error('Max retries exceeded');
152
- }
153
-
154
168
  try {
155
169
  console.log(`🔧 Trying strategy: ${strategy.name}`);
156
170
  await strategy.fn();
@@ -281,7 +295,7 @@ export class ConnectionRecovery {
281
295
  console.log('🔄 Recovery state reset');
282
296
  }
283
297
 
284
- // Cleanup
298
+ // Cleanup - setzt isRecovering auf false, damit die Retry-Schleife stoppt
285
299
  cleanup() {
286
300
  this.stopHealthMonitoring();
287
301
  this.state.isRecovering = false;
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];