@vielhuber/wahelper 1.5.8 → 1.6.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/package.json +1 -1
- package/wahelper-daemon.js +67 -2
- package/wahelper.js +21 -6
package/package.json
CHANGED
package/wahelper-daemon.js
CHANGED
|
@@ -33,6 +33,7 @@ export default class wahelperDaemon {
|
|
|
33
33
|
this.qr = null;
|
|
34
34
|
this.pairingCode = null;
|
|
35
35
|
this.pairingCodeRequested = false;
|
|
36
|
+
this.lastError = null;
|
|
36
37
|
this.isFirstRun = false;
|
|
37
38
|
this.reconnectDelay = 1000;
|
|
38
39
|
this.httpServer = null;
|
|
@@ -129,9 +130,14 @@ export default class wahelperDaemon {
|
|
|
129
130
|
content TEXT,
|
|
130
131
|
media_data TEXT,
|
|
131
132
|
media_filename TEXT,
|
|
132
|
-
timestamp INTEGER
|
|
133
|
+
timestamp INTEGER,
|
|
134
|
+
\`read\` INTEGER NOT NULL DEFAULT 0
|
|
133
135
|
);
|
|
134
136
|
`);
|
|
137
|
+
let cols = this.db.prepare('PRAGMA table_info(messages)').all();
|
|
138
|
+
if (!cols.some(c => c.name === 'read')) {
|
|
139
|
+
this.db.exec('ALTER TABLE messages ADD COLUMN `read` INTEGER NOT NULL DEFAULT 0');
|
|
140
|
+
}
|
|
135
141
|
} catch (error) {
|
|
136
142
|
this.log('⛔ Error initing database: ' + error.message + ' (code: ' + error.code + ')');
|
|
137
143
|
}
|
|
@@ -394,6 +400,42 @@ export default class wahelperDaemon {
|
|
|
394
400
|
this.dbLock = false;
|
|
395
401
|
}
|
|
396
402
|
|
|
403
|
+
async markMessagesRead(updates) {
|
|
404
|
+
if (!Array.isArray(updates) || updates.length === 0) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
// WAMessageStatus.READ === 4
|
|
408
|
+
let ids = updates
|
|
409
|
+
.filter(u => u?.update?.status === 4)
|
|
410
|
+
.map(u => u?.key?.id)
|
|
411
|
+
.filter(id => typeof id === 'string' && id.length > 0);
|
|
412
|
+
if (ids.length === 0) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
while (this.dbLock) {
|
|
416
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
417
|
+
}
|
|
418
|
+
this.dbLock = true;
|
|
419
|
+
try {
|
|
420
|
+
if (this.dbIsOpen === false) {
|
|
421
|
+
this.initDatabase();
|
|
422
|
+
}
|
|
423
|
+
let stmt = this.db.prepare('UPDATE messages SET `read` = 1 WHERE id = ? AND `read` = 0');
|
|
424
|
+
let touched = 0;
|
|
425
|
+
for (let id of ids) {
|
|
426
|
+
let res = stmt.run(id);
|
|
427
|
+
if (res && res.changes > 0) touched++;
|
|
428
|
+
}
|
|
429
|
+
if (touched > 0) {
|
|
430
|
+
this.log('marked ' + touched + '/' + ids.length + ' messages as read');
|
|
431
|
+
}
|
|
432
|
+
} catch (error) {
|
|
433
|
+
this.log('⛔ markMessagesRead failed: ' + error.message);
|
|
434
|
+
} finally {
|
|
435
|
+
this.dbLock = false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
397
439
|
async sendMessageToUser(number = null, message = null, attachments = null) {
|
|
398
440
|
if (!this.connected || !this.sock) {
|
|
399
441
|
throw new Error('not_connected');
|
|
@@ -502,6 +544,14 @@ export default class wahelperDaemon {
|
|
|
502
544
|
await this.storeDataToDatabase(obj);
|
|
503
545
|
});
|
|
504
546
|
|
|
547
|
+
this.sock.ev.on('messages.update', async updates => {
|
|
548
|
+
try {
|
|
549
|
+
await this.markMessagesRead(updates);
|
|
550
|
+
} catch (error) {
|
|
551
|
+
this.log('⛔ messages.update handler failed: ' + error.message);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
505
555
|
this.sock.ev.on('creds.update', saveCreds);
|
|
506
556
|
|
|
507
557
|
this.sock.ev.on('connection.update', async update => {
|
|
@@ -522,6 +572,7 @@ export default class wahelperDaemon {
|
|
|
522
572
|
console.log('\nPairing code: ' + code);
|
|
523
573
|
})
|
|
524
574
|
.catch(err => {
|
|
575
|
+
this.lastError = { source: 'pairing', message: err.message, at: Date.now() };
|
|
525
576
|
this.log('Pairing code request failed: ' + err.message);
|
|
526
577
|
});
|
|
527
578
|
}
|
|
@@ -560,6 +611,17 @@ export default class wahelperDaemon {
|
|
|
560
611
|
}
|
|
561
612
|
|
|
562
613
|
// reconnect on all other disconnect reasons with exponential backoff
|
|
614
|
+
let reason =
|
|
615
|
+
DisconnectReason && statusCode
|
|
616
|
+
? Object.keys(DisconnectReason).find(k => DisconnectReason[k] === statusCode)
|
|
617
|
+
: null;
|
|
618
|
+
this.lastError = {
|
|
619
|
+
source: 'disconnect',
|
|
620
|
+
message:
|
|
621
|
+
'connection closed' +
|
|
622
|
+
(reason ? ' (' + reason + ')' : statusCode ? ' (statusCode=' + statusCode + ')' : ''),
|
|
623
|
+
at: Date.now()
|
|
624
|
+
};
|
|
563
625
|
let delay = this.reconnectDelay;
|
|
564
626
|
this.log('Reconnecting in ' + delay + 'ms');
|
|
565
627
|
console.log('Reconnecting in ' + delay + 'ms...');
|
|
@@ -575,6 +637,7 @@ export default class wahelperDaemon {
|
|
|
575
637
|
this.qr = null;
|
|
576
638
|
this.pairingCode = null;
|
|
577
639
|
this.pairingCodeRequested = false;
|
|
640
|
+
this.lastError = null;
|
|
578
641
|
this.reconnectDelay = 1000;
|
|
579
642
|
this.log('✅ Connected');
|
|
580
643
|
console.log('✅ Connected (device: ' + this.device + ')');
|
|
@@ -584,6 +647,7 @@ export default class wahelperDaemon {
|
|
|
584
647
|
})
|
|
585
648
|
.catch(error => {
|
|
586
649
|
this.connecting = false;
|
|
650
|
+
this.lastError = { source: 'connect', message: error.message, at: Date.now() };
|
|
587
651
|
this.log('⛔ Connect error: ' + error.message);
|
|
588
652
|
console.log('⛔ Connect error: ' + error.message);
|
|
589
653
|
setTimeout(() => this.connect(), this.reconnectDelay);
|
|
@@ -619,7 +683,8 @@ export default class wahelperDaemon {
|
|
|
619
683
|
connected: this.connected,
|
|
620
684
|
device: this.device,
|
|
621
685
|
qr: this.qr,
|
|
622
|
-
pairingCode: this.pairingCode
|
|
686
|
+
pairingCode: this.pairingCode,
|
|
687
|
+
lastError: this.lastError
|
|
623
688
|
});
|
|
624
689
|
return;
|
|
625
690
|
}
|
package/wahelper.js
CHANGED
|
@@ -95,11 +95,15 @@ export default class wahelper {
|
|
|
95
95
|
console.log('\n⚠️ Pairing required. Scan the QR code with WhatsApp, then retry.\n');
|
|
96
96
|
console.log(daemonStatus.qrString);
|
|
97
97
|
}
|
|
98
|
+
if (daemonStatus.message === 'daemon_error' || daemonStatus.message === 'daemon_timeout') {
|
|
99
|
+
console.log('⛔ ' + (daemonStatus.public_message || daemonStatus.message));
|
|
100
|
+
}
|
|
98
101
|
this.write(
|
|
99
102
|
{
|
|
100
103
|
success: false,
|
|
101
104
|
message: daemonStatus.message,
|
|
102
|
-
|
|
105
|
+
public_message: daemonStatus.public_message || null,
|
|
106
|
+
data: daemonStatus.data || daemonStatus.pairingCode || daemonStatus.qrString || null
|
|
103
107
|
},
|
|
104
108
|
true
|
|
105
109
|
);
|
|
@@ -133,7 +137,7 @@ export default class wahelper {
|
|
|
133
137
|
let messages = this.db
|
|
134
138
|
.prepare(
|
|
135
139
|
`
|
|
136
|
-
SELECT id, \`from\`, \`to\`, content, media_filename, timestamp
|
|
140
|
+
SELECT id, \`from\`, \`to\`, content, media_filename, timestamp, \`read\`
|
|
137
141
|
FROM messages
|
|
138
142
|
ORDER BY timestamp DESC
|
|
139
143
|
${limit !== null ? 'LIMIT ' + limit : ''}
|
|
@@ -166,7 +170,7 @@ export default class wahelper {
|
|
|
166
170
|
this.db
|
|
167
171
|
.prepare(
|
|
168
172
|
`
|
|
169
|
-
SELECT id, \`from\`, \`to\`, content, media_filename, timestamp
|
|
173
|
+
SELECT id, \`from\`, \`to\`, content, media_filename, timestamp, \`read\`
|
|
170
174
|
FROM messages
|
|
171
175
|
WHERE id = ?
|
|
172
176
|
LIMIT 1
|
|
@@ -251,14 +255,20 @@ export default class wahelper {
|
|
|
251
255
|
return { connected: false, message: 'pairing_required', pairingCode: status.pairingCode };
|
|
252
256
|
}
|
|
253
257
|
if (status.qr) {
|
|
254
|
-
// render QR to ASCII string for display
|
|
255
258
|
let qrString = await new Promise(resolve => {
|
|
256
259
|
qrcodeTerminal.generate(status.qr, { small: true }, str => resolve('\n' + str));
|
|
257
260
|
});
|
|
258
261
|
return { connected: false, message: 'qr_required', qrString };
|
|
259
262
|
}
|
|
263
|
+
if (status.lastError) {
|
|
264
|
+
return {
|
|
265
|
+
connected: false,
|
|
266
|
+
message: 'daemon_error',
|
|
267
|
+
public_message: status.lastError.source + ': ' + status.lastError.message,
|
|
268
|
+
data: status.lastError
|
|
269
|
+
};
|
|
270
|
+
}
|
|
260
271
|
}
|
|
261
|
-
|
|
262
272
|
return { connected: false, message: 'daemon_timeout' };
|
|
263
273
|
}
|
|
264
274
|
|
|
@@ -487,9 +497,14 @@ export default class wahelper {
|
|
|
487
497
|
content TEXT,
|
|
488
498
|
media_data TEXT,
|
|
489
499
|
media_filename TEXT,
|
|
490
|
-
timestamp INTEGER
|
|
500
|
+
timestamp INTEGER,
|
|
501
|
+
\`read\` INTEGER NOT NULL DEFAULT 0
|
|
491
502
|
);
|
|
492
503
|
`);
|
|
504
|
+
let cols = this.db.prepare('PRAGMA table_info(messages)').all();
|
|
505
|
+
if (!cols.some(c => c.name === 'read')) {
|
|
506
|
+
this.db.exec('ALTER TABLE messages ADD COLUMN `read` INTEGER NOT NULL DEFAULT 0');
|
|
507
|
+
}
|
|
493
508
|
} catch (error) {
|
|
494
509
|
this.log('⛔ Error initing database: ' + error.message + ' (code: ' + error.code + ')');
|
|
495
510
|
}
|