@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielhuber/wahelper",
3
- "version": "1.5.8",
3
+ "version": "1.6.0",
4
4
  "description": "Lightweight whatsapp integration layer.",
5
5
  "main": "wahelper.js",
6
6
  "files": [
@@ -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
- data: daemonStatus.pairingCode || daemonStatus.qrString || null
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
  }