whalibmob 5.14.12 → 5.14.13

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.
Files changed (3) hide show
  1. package/README.md +9 -8
  2. package/cli.js +78 -34
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -430,16 +430,17 @@ The CLI asks what to call it once, on a first run, and remembers the answer in
430
430
 
431
431
  ### CLI Pairing Code
432
432
 
433
- If the number is already in use on a phone, or the verification SMS never arrives, link to the existing account instead. When it is not obvious which way you mean, `wa connect` asks:
433
+ If the number is already in use on a phone, or the verification SMS never arrives, link to the existing account instead.
434
+
435
+ `wa connect` works out which of the two a number is set up for by reading its session files, and says which one it chose:
434
436
 
435
437
  ```
436
- how do you want to connect?
437
- 1) sms register this number as its own device
438
- 2) pairing code link to an existing WhatsApp account (8-digit code)
439
- sms or pairing code? [1/2] 2
438
+ connecting as companion (pairing code)...
440
439
  ```
441
440
 
442
- The question is skipped when only one kind of session exists on disk, and when stdin is not a terminal. Force either one:
441
+ A number can hold both — registered over SMS as its own device, and linked as a companion to another account. When it holds both, the one used most recently wins. A half-finished registration does not count as a session either way. When there is nothing to connect with, it says so and names the two commands that would create one, rather than picking a side.
442
+
443
+ Force either one:
443
444
 
444
445
  ```sh
445
446
  wa connect 919634847671 --sms
@@ -1430,7 +1431,7 @@ the session rather than the environment.
1430
1431
 
1431
1432
  ```sh
1432
1433
  # connect to a number (while already in the shell)
1433
- # asks sms or pairing code when both are possible
1434
+ # picks sms or pairing from the session files on disk
1434
1435
  wa> /connect 919634847671
1435
1436
 
1436
1437
  # force one or the other
@@ -1563,7 +1564,7 @@ wa> /quit
1563
1564
  | `/reg code <phone> [method] [--name "Name"]` | Request verification code, optionally naming the account |
1564
1565
  | `/reg confirm <phone> <code> [--name "Name"]` | Complete registration |
1565
1566
  | **Connection** | |
1566
- | `/connect <phone> [sms\|pair]` | Connect to WhatsApp — asks which method when unset |
1567
+ | `/connect <phone> [sms\|pair]` | Connect to WhatsApp — picks the method from the session files when unset |
1567
1568
  | `/pair <phone> [code]` | Link to an existing account by 8-digit pairing code |
1568
1569
  | `/disconnect` | Disconnect current session |
1569
1570
  | `/reconnect` | Force reconnection |
package/cli.js CHANGED
@@ -900,41 +900,60 @@ function openShell(prompt) {
900
900
  // Which way in: register this number over SMS as the account's own device, or
901
901
  // link to an account that already exists the way WhatsApp Web does.
902
902
  //
903
- // Asked only when it is genuinely ambiguous. If exactly one kind of session is
904
- // already on disk that one wins, and a non-interactive stdin never blocks on a
905
- // question nobody is there to answer.
906
- function hasMobileSession(phone) {
907
- return fs.existsSync(storeFileFor(_sessDir, phone));
903
+ // Which kind of session a number has, read off the file rather than guessed.
904
+ //
905
+ // A number can hold both — registered over SMS as its own device, and linked
906
+ // as a companion to some other account — so "the file is there" was never
907
+ // enough to go on. It is also not enough on its own: a registration that was
908
+ // started and never finished leaves a store behind with `registered` still
909
+ // false, and connecting with it can only fail.
910
+ //
911
+ // Returns null when there is nothing usable, otherwise when the session was
912
+ // last written. That timestamp is the tie-breaker below.
913
+ function readSessionKind(file, extra) {
914
+ if (!fs.existsSync(file)) return null;
915
+ try {
916
+ const j = JSON.parse(fs.readFileSync(file, 'utf8'));
917
+ if (!j.registered) return null;
918
+ if (extra && !extra(j)) return null;
919
+ return { mtime: fs.statSync(file).mtimeMs };
920
+ } catch (_) { return null; }
908
921
  }
909
922
 
910
- function hasWebSession(phone) {
911
- const f = webStoreFileFor(_sessDir, phone);
912
- if (!fs.existsSync(f)) return false;
913
- try {
914
- const j = JSON.parse(fs.readFileSync(f, 'utf8'));
915
- return !!(j.registered && j.me && j.me.id);
916
- } catch (_) { return false; }
923
+ function mobileSession(phone) {
924
+ return readSessionKind(storeFileFor(_sessDir, phone));
917
925
  }
918
926
 
919
- function askLoginMethod(phone) {
920
- const mob = hasMobileSession(phone);
921
- const web = hasWebSession(phone);
922
- if (web && !mob) return Promise.resolve('pairing');
923
- if (mob && !web) return Promise.resolve('sms');
924
- if (!process.stdin.isTTY) return Promise.resolve(mob ? 'sms' : 'pairing');
927
+ function webSession(phone) {
928
+ return readSessionKind(webStoreFileFor(_sessDir, phone), j => !!(j.me && j.me.id));
929
+ }
925
930
 
926
- return new Promise(resolve => {
927
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
928
- out('');
929
- out(' how do you want to connect?');
930
- out(' 1) sms register this number as its own device');
931
- out(' 2) pairing code link to an existing WhatsApp account (8-digit code)');
932
- rl.question(' sms or pairing code? [1/2] ', (answer) => {
933
- rl.close();
934
- const a = String(answer).trim().toLowerCase();
935
- resolve((a === '2' || a.startsWith('p')) ? 'pairing' : 'sms');
936
- });
937
- });
931
+ function hasMobileSession(phone) { return !!mobileSession(phone); }
932
+ function hasWebSession(phone) { return !!webSession(phone); }
933
+
934
+ /**
935
+ * Work out how to connect a number, without asking.
936
+ *
937
+ * This used to put a question on the terminal, and the question was worse than
938
+ * useless: it opened a second reader on the same stdin while the shell's own
939
+ * was still running, so the two split the answer between them and the one that
940
+ * mattered usually got nothing. An unrecognised answer then fell through to
941
+ * "sms" — the more destructive of the two — and a companion session that was
942
+ * perfectly good was met with a login as a primary device, which the server
943
+ * answers with a 401 that reads exactly like a revoked session.
944
+ *
945
+ * There is nothing to ask. The session files say which kinds exist, and when
946
+ * both do, the one used most recently is the one being asked for.
947
+ *
948
+ * @returns {'sms'|'pairing'|null} null when the number has no usable session
949
+ */
950
+ function resolveLoginMethod(phone) {
951
+ const mob = mobileSession(phone);
952
+ const web = webSession(phone);
953
+ if (mob && !web) return 'sms';
954
+ if (web && !mob) return 'pairing';
955
+ if (!mob && !web) return null;
956
+ return web.mtime >= mob.mtime ? 'pairing' : 'sms';
938
957
  }
939
958
 
940
959
  // Handlers for the two things a registration can stop and ask for.
@@ -945,8 +964,18 @@ function askLoginMethod(phone) {
945
964
  function registrationPrompts() {
946
965
  const prompt = (question) => new Promise((resolve) => {
947
966
  if (!process.stdin.isTTY) return resolve(null);
967
+ // The shell's own reader has to stand down first. Two readline interfaces
968
+ // on one stdin both take the keypresses, so the answer is split between
969
+ // them: the shell treats it as a command and this one is left with
970
+ // nothing. Every other place that asks something mid-session already does
971
+ // this — the two that did not were where the answer went missing.
972
+ _rl && _rl.pause();
948
973
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
949
- rl.question(question, (answer) => { rl.close(); resolve(String(answer).trim()); });
974
+ rl.question(question, (answer) => {
975
+ rl.close();
976
+ _rl && (_rl.resume(), _rl.prompt(true));
977
+ resolve(String(answer).trim());
978
+ });
950
979
  });
951
980
 
952
981
  return {
@@ -1229,8 +1258,17 @@ async function handleLine(line) {
1229
1258
  const forced = (p[2] || '').toLowerCase();
1230
1259
  const method = forced === 'pair' || forced === 'pairing' ? 'pairing'
1231
1260
  : forced === 'sms' ? 'sms'
1232
- : await askLoginMethod(phn);
1233
- out('connecting...');
1261
+ : resolveLoginMethod(phn);
1262
+ // Nothing on disk to connect with. Say which of the two things to do
1263
+ // rather than picking one and letting the server explain it as a 401.
1264
+ if (!method) {
1265
+ fail('no session for +' + phn);
1266
+ out(' register it as its own device: /reg code ' + phn);
1267
+ out(' or link it to an account: /pair ' + phn);
1268
+ break;
1269
+ }
1270
+ out('connecting as ' + (method === 'pairing' ? 'companion (pairing code)'
1271
+ : 'primary device (sms)') + '...');
1234
1272
  if (method === 'pairing') await doConnectWeb(phn);
1235
1273
  else await doConnect(phn);
1236
1274
  break;
@@ -2943,7 +2981,13 @@ async function main() {
2943
2981
  const forced = String(flags.method || '').toLowerCase();
2944
2982
  const method = flags.pair || forced === 'pair' || forced === 'pairing' ? 'pairing'
2945
2983
  : flags.sms || forced === 'sms' ? 'sms'
2946
- : await askLoginMethod(phone);
2984
+ : resolveLoginMethod(phone);
2985
+ if (!method) {
2986
+ fail('no session for +' + phone);
2987
+ out(' register it as its own device: wa registration --request-code ' + phone);
2988
+ out(' or link it to an account: wa pair ' + phone);
2989
+ process.exit(1);
2990
+ }
2947
2991
  // Plain `wa>` until the connection actually opens. Naming the number in the
2948
2992
  // prompt before that says "connected as this number" while the line above
2949
2993
  // it says there is no session, which is the opposite of what happened —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.14.12",
3
+ "version": "5.14.13",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API and web ",
5
5
  "author": "Kunboruto20",
6
6
  "main": "index.js",