@vielhuber/wahelper 1.8.7 → 1.8.9

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
@@ -53,6 +53,9 @@ npx wahelper \
53
53
  --device "xxxxxxxxxxxx" \
54
54
  ...
55
55
 
56
+ # check the live connection
57
+ --action "get_status"
58
+
56
59
  # fetch messages
57
60
  --action "fetch_messages" \
58
61
  --filter '{"from":"491234567890","to":"491234567890","message":"meeting","date_from":"2026-01-01","date_until":"2026-12-31"}' \
@@ -85,6 +88,9 @@ use vielhuber\wahelper\wahelper;
85
88
 
86
89
  $wahelper = new wahelper();
87
90
 
91
+ // success is true only when connected
92
+ $status = $wahelper->getStatus(device: 'xxxxxxxxxxxx');
93
+
88
94
  // fetch messages
89
95
  $wahelper->fetchMessages(
90
96
  device: 'xxxxxxxxxxxx',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielhuber/wahelper",
3
- "version": "1.8.7",
3
+ "version": "1.8.9",
4
4
  "description": "Lightweight whatsapp integration layer.",
5
5
  "main": "wahelper.js",
6
6
  "files": [
@@ -36,6 +36,7 @@ export default class wahelperDaemon {
36
36
  this.dbLock = false;
37
37
  this.connected = false;
38
38
  this.connecting = false;
39
+ this.loggedOut = false;
39
40
  this.qr = null;
40
41
  this.pairingCode = null;
41
42
  this.pairingCodeRequested = false;
@@ -698,8 +699,8 @@ export default class wahelperDaemon {
698
699
  }
699
700
 
700
701
  async sendMessageToUser(number = null, message = null, attachments = null) {
701
- if (!this.connected || !this.sock) {
702
- throw new Error('not_connected');
702
+ if (!this.getStatus().connected) {
703
+ throw new Error(this.loggedOut ? 'logged_out' : 'not_connected');
703
704
  }
704
705
  // validate all attachments exist before sending anything
705
706
  if (attachments !== null && attachments.length > 0) {
@@ -724,8 +725,8 @@ export default class wahelperDaemon {
724
725
  }
725
726
 
726
727
  async sendMessageToGroup(name = null, message = null, attachments = null) {
727
- if (!this.connected || !this.sock) {
728
- throw new Error('not_connected');
728
+ if (!this.getStatus().connected) {
729
+ throw new Error(this.loggedOut ? 'logged_out' : 'not_connected');
729
730
  }
730
731
  // validate all attachments exist before sending anything
731
732
  if (attachments !== null && attachments.length > 0) {
@@ -756,7 +757,7 @@ export default class wahelperDaemon {
756
757
  }
757
758
 
758
759
  connect() {
759
- if (this.connecting) {
760
+ if (this.connecting || this.loggedOut) {
760
761
  return;
761
762
  }
762
763
  this.connecting = true;
@@ -827,7 +828,8 @@ export default class wahelperDaemon {
827
828
  // sync full history only during initial pairing and its required reconnect
828
829
  syncFullHistory: !state.creds.registered || this.isFirstRun,
829
830
  version,
830
- browser: Browsers.windows('Desktop')
831
+ // whatsapp rejects new logins as "Desktop" with full history sync (428 connectionClosed)
832
+ browser: Browsers.windows('Chrome')
831
833
  });
832
834
  this.sock = socket;
833
835
  staleSocket = null;
@@ -885,10 +887,16 @@ export default class wahelperDaemon {
885
887
  socket
886
888
  .requestPairingCode(this.device)
887
889
  .then(code => {
890
+ if (this.sock !== socket) {
891
+ return;
892
+ }
888
893
  this.pairingCode = code;
889
894
  console.log('\nPairing code: ' + code);
890
895
  })
891
896
  .catch(err => {
897
+ if (this.sock !== socket) {
898
+ return;
899
+ }
892
900
  this.lastError = { source: 'pairing', message: err.message, at: Date.now() };
893
901
  this.log('Pairing code request failed: ' + err.message);
894
902
  });
@@ -917,16 +925,19 @@ export default class wahelperDaemon {
917
925
  }
918
926
 
919
927
  if (statusCode === DisconnectReason.loggedOut) {
920
- // logged out — delete auth and reconnect
928
+ this.loggedOut = true;
929
+ this.sock = null;
921
930
  this.qr = null;
922
931
  this.pairingCode = null;
923
932
  this.pairingCodeRequested = false;
924
- this.log('Logged out, removing auth folder');
925
- console.log('Logged out, removing auth folder...');
926
- if (fs.existsSync(this.dirname + '/' + this.authFolder)) {
927
- fs.rmSync(this.dirname + '/' + this.authFolder, { recursive: true, force: true });
928
- }
929
- setTimeout(() => this.connect(), 1000);
933
+ this.lastError = {
934
+ source: 'disconnect',
935
+ message: 'Logged out. Automatic reconnect stopped; manual device linking required.',
936
+ statusCode,
937
+ at: Date.now()
938
+ };
939
+ this.log(this.lastError);
940
+ console.error(this.lastError.message);
930
941
  return;
931
942
  }
932
943
 
@@ -949,10 +960,16 @@ export default class wahelperDaemon {
949
960
  source: 'disconnect',
950
961
  message:
951
962
  'connection closed' +
952
- (reason ? ' (' + reason + ')' : statusCode ? ' (statusCode=' + statusCode + ')' : ''),
963
+ (reason
964
+ ? ' (' + reason + ')'
965
+ : statusCode
966
+ ? ' (statusCode=' + statusCode + ')'
967
+ : ''),
968
+ statusCode,
953
969
  at: Date.now()
954
970
  };
955
971
  }
972
+ this.log(this.lastError);
956
973
  this.consecutiveFailures++;
957
974
  // first 3 attempts recover network blips fast (1s/2s/4s),
958
975
  // after that back off to 15min so a persistent failure
@@ -1000,6 +1017,19 @@ export default class wahelperDaemon {
1000
1017
  });
1001
1018
  }
1002
1019
 
1020
+ getStatus() {
1021
+ return {
1022
+ success: true,
1023
+ connected: this.connected && this.sock?.ws?.isOpen === true,
1024
+ connecting: this.connecting,
1025
+ loggedOut: this.loggedOut,
1026
+ device: this.device,
1027
+ qr: this.qr,
1028
+ pairingCode: this.pairingCode,
1029
+ lastError: this.lastError
1030
+ };
1031
+ }
1032
+
1003
1033
  startHttpServer() {
1004
1034
  this.httpServer = http.createServer(async (req, res) => {
1005
1035
  // bound to 127.0.0.1 below — every request additionally has to
@@ -1025,14 +1055,7 @@ export default class wahelperDaemon {
1025
1055
  this.sendJsonResponse(res, 403, { success: false, message: 'forbidden' });
1026
1056
  return;
1027
1057
  }
1028
- this.sendJsonResponse(res, 200, {
1029
- success: true,
1030
- connected: this.connected,
1031
- device: this.device,
1032
- qr: this.qr,
1033
- pairingCode: this.pairingCode,
1034
- lastError: this.lastError
1035
- });
1058
+ this.sendJsonResponse(res, 200, this.getStatus());
1036
1059
  return;
1037
1060
  }
1038
1061
 
package/wahelper.js CHANGED
@@ -53,6 +53,28 @@ export default class wahelper {
53
53
  }
54
54
 
55
55
  async init() {
56
+ if (this.args.action === 'get_status') {
57
+ let status = this.args.device
58
+ ? await this.callDaemon('GET', '/status')
59
+ : { success: false, message: 'missing_device' };
60
+ let connected = status.connected === true;
61
+ let response = {
62
+ success: connected,
63
+ message: connected ? 'connected' : status.loggedOut ? 'logged_out' : status.message || 'not_connected',
64
+ public_message: status.lastError?.message ?? null,
65
+ data: {
66
+ connected,
67
+ connecting: status.connecting === true,
68
+ loggedOut: status.loggedOut === true,
69
+ pairingRequired: status.loggedOut === true || Boolean(status.pairingCode || status.qr),
70
+ lastError: status.lastError ?? null
71
+ }
72
+ };
73
+ this.write(response, false);
74
+ console.log(JSON.stringify(response));
75
+ process.exitCode = connected ? 0 : 1;
76
+ return;
77
+ }
56
78
  await this.awaitLock('init', true);
57
79
  this.setLock('init', true);
58
80
  this.write({ success: false, message: 'loading_state', data: null }, false);
@@ -127,7 +149,11 @@ export default class wahelper {
127
149
  console.log('\n⚠️ Pairing required. Scan the QR code with WhatsApp, then retry.\n');
128
150
  console.log(daemonStatus.qrString);
129
151
  }
130
- if (daemonStatus.message === 'daemon_error' || daemonStatus.message === 'daemon_timeout') {
152
+ if (
153
+ daemonStatus.message === 'daemon_error' ||
154
+ daemonStatus.message === 'daemon_timeout' ||
155
+ daemonStatus.message === 'logged_out'
156
+ ) {
131
157
  console.log('⛔ ' + (daemonStatus.public_message || daemonStatus.message));
132
158
  }
133
159
  this.write(
@@ -316,26 +342,25 @@ export default class wahelper {
316
342
  }
317
343
 
318
344
  async ensureDaemon() {
319
- // check current status
320
- let status = await this.callDaemon('GET', '/status');
321
-
322
- // daemon not reachable — return error immediately, daemon must be started manually
323
- if (status.message === 'daemon_not_reachable') {
324
- return { connected: false, message: 'daemon_not_running' };
325
- }
326
-
327
- if (status.connected) {
328
- return status;
329
- }
330
-
331
- // poll up to 30s — return immediately when pairing code appears
332
- console.log('Waiting for daemon to connect...');
333
- for (let i = 0; i < 30; i++) {
334
- await new Promise(resolve => setTimeout(resolve, 1000));
335
- status = await this.callDaemon('GET', '/status');
345
+ for (let i = 0; i <= 30; i++) {
346
+ let status = await this.callDaemon('GET', '/status');
347
+ if (status.success === false) {
348
+ return {
349
+ connected: false,
350
+ message: status.message === 'daemon_not_reachable' ? 'daemon_not_running' : status.message
351
+ };
352
+ }
336
353
  if (status.connected) {
337
354
  return status;
338
355
  }
356
+ if (status.loggedOut) {
357
+ return {
358
+ connected: false,
359
+ message: 'logged_out',
360
+ public_message: status.lastError?.message,
361
+ data: status.lastError
362
+ };
363
+ }
339
364
  if (status.pairingCode) {
340
365
  return { connected: false, message: 'pairing_required', pairingCode: status.pairingCode };
341
366
  }
@@ -353,6 +378,12 @@ export default class wahelper {
353
378
  data: status.lastError
354
379
  };
355
380
  }
381
+ if (i === 0) {
382
+ console.log('Waiting for daemon to connect...');
383
+ }
384
+ if (i < 30) {
385
+ await new Promise(resolve => setTimeout(resolve, 1000));
386
+ }
356
387
  }
357
388
  return { connected: false, message: 'daemon_timeout' };
358
389
  }
@@ -400,7 +431,7 @@ export default class wahelper {
400
431
  }
401
432
  });
402
433
  });
403
- req.setTimeout(30000, () => {
434
+ req.setTimeout(path === '/status' ? 5000 : 30000, () => {
404
435
  req.destroy();
405
436
  this.log('⛔ Daemon request timeout');
406
437
  resolve({ success: false, message: 'daemon_timeout' });
@@ -642,6 +673,6 @@ if (
642
673
  ) {
643
674
  let wa = new wahelper();
644
675
  wa.init()
645
- .then(() => process.exit(0))
676
+ .then(() => process.exit(process.exitCode ?? 0))
646
677
  .catch(() => process.exit(1));
647
678
  }