waengine 2.4.9 → 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 +82 -2
- package/package.json +1 -1
- package/src/client.js +131 -2
- package/src/connection-recovery.js +15 -11
- package/src/message.js +94 -0
package/README.md
CHANGED
|
@@ -1,4 +1,84 @@
|
|
|
1
|
-
# 🚀 WAEngine
|
|
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
|
[](https://www.npmjs.com/package/waengine)
|
|
4
84
|
[](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:** [
|
|
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.
|
|
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",
|
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:
|
|
1309
|
+
type: msgType,
|
|
1188
1310
|
isGroup: msg.key.remoteJid?.includes("@g.us"),
|
|
1189
|
-
participant: msg.key.participant,
|
|
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
|
}
|
|
@@ -128,12 +128,20 @@ export class ConnectionRecovery {
|
|
|
128
128
|
|
|
129
129
|
console.log('🛡️ Initiating connection recovery...');
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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;
|
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];
|