@xbibzlibrary/whatsbibz 1.3.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.
Files changed (260) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/LICENSE +22 -0
  3. package/LICENSE.upstream +21 -0
  4. package/README.md +280 -0
  5. package/VERIFIKASI-IDENTITAS-2026-09-03.md +138 -0
  6. package/WAProto/GenerateStatics.sh +3 -0
  7. package/WAProto/WAProto.proto +6035 -0
  8. package/WAProto/fix-imports.js +81 -0
  9. package/WAProto/index.d.ts +15314 -0
  10. package/WAProto/index.js +132326 -0
  11. package/assets/logo/README.md +21 -0
  12. package/assets/logo/png/banner-1200x360.png +0 -0
  13. package/assets/logo/png/icon-1024.png +0 -0
  14. package/assets/logo/png/icon-128.png +0 -0
  15. package/assets/logo/png/icon-16.png +0 -0
  16. package/assets/logo/png/icon-24.png +0 -0
  17. package/assets/logo/png/icon-256.png +0 -0
  18. package/assets/logo/png/icon-32.png +0 -0
  19. package/assets/logo/png/icon-48.png +0 -0
  20. package/assets/logo/png/icon-512.png +0 -0
  21. package/assets/logo/png/icon-64.png +0 -0
  22. package/assets/logo/png/mark-512.png +0 -0
  23. package/assets/logo/preview.png +0 -0
  24. package/assets/logo/whatsbibz.ico +0 -0
  25. package/lib/BibzWhats/client.d.ts +95 -0
  26. package/lib/BibzWhats/client.js +501 -0
  27. package/lib/BibzWhats/device-identity.d.ts +60 -0
  28. package/lib/BibzWhats/device-identity.js +234 -0
  29. package/lib/BibzWhats/extract.d.ts +29 -0
  30. package/lib/BibzWhats/extract.js +115 -0
  31. package/lib/BibzWhats/index.d.ts +7 -0
  32. package/lib/BibzWhats/index.js +8 -0
  33. package/lib/BibzWhats/jid.d.ts +22 -0
  34. package/lib/BibzWhats/jid.js +98 -0
  35. package/lib/BibzWhats/pairing.d.ts +35 -0
  36. package/lib/BibzWhats/pairing.js +186 -0
  37. package/lib/BibzWhats/send.d.ts +9 -0
  38. package/lib/BibzWhats/send.js +107 -0
  39. package/lib/BibzWhats/version.d.ts +6 -0
  40. package/lib/BibzWhats/version.js +7 -0
  41. package/lib/Defaults/index.d.ts +73 -0
  42. package/lib/Defaults/index.js +141 -0
  43. package/lib/Modded/message_builder.d.ts +15 -0
  44. package/lib/Modded/message_builder.js +2330 -0
  45. package/lib/Signal/Group/ciphertext-message.d.ts +9 -0
  46. package/lib/Signal/Group/ciphertext-message.js +11 -0
  47. package/lib/Signal/Group/group-session-builder.d.ts +14 -0
  48. package/lib/Signal/Group/group-session-builder.js +29 -0
  49. package/lib/Signal/Group/group_cipher.d.ts +16 -0
  50. package/lib/Signal/Group/group_cipher.js +81 -0
  51. package/lib/Signal/Group/index.d.ts +11 -0
  52. package/lib/Signal/Group/index.js +11 -0
  53. package/lib/Signal/Group/keyhelper.d.ts +10 -0
  54. package/lib/Signal/Group/keyhelper.js +17 -0
  55. package/lib/Signal/Group/sender-chain-key.d.ts +13 -0
  56. package/lib/Signal/Group/sender-chain-key.js +25 -0
  57. package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -0
  58. package/lib/Signal/Group/sender-key-distribution-message.js +62 -0
  59. package/lib/Signal/Group/sender-key-message.d.ts +18 -0
  60. package/lib/Signal/Group/sender-key-message.js +65 -0
  61. package/lib/Signal/Group/sender-key-name.d.ts +17 -0
  62. package/lib/Signal/Group/sender-key-name.js +47 -0
  63. package/lib/Signal/Group/sender-key-record.d.ts +30 -0
  64. package/lib/Signal/Group/sender-key-record.js +40 -0
  65. package/lib/Signal/Group/sender-key-state.d.ts +38 -0
  66. package/lib/Signal/Group/sender-key-state.js +83 -0
  67. package/lib/Signal/Group/sender-message-key.d.ts +11 -0
  68. package/lib/Signal/Group/sender-message-key.js +25 -0
  69. package/lib/Signal/libsignal.d.ts +4 -0
  70. package/lib/Signal/libsignal.js +406 -0
  71. package/lib/Signal/lid-mapping.d.ts +18 -0
  72. package/lib/Signal/lid-mapping.js +276 -0
  73. package/lib/Socket/Client/index.d.ts +2 -0
  74. package/lib/Socket/Client/index.js +2 -0
  75. package/lib/Socket/Client/types.d.ts +15 -0
  76. package/lib/Socket/Client/types.js +10 -0
  77. package/lib/Socket/Client/websocket.d.ts +12 -0
  78. package/lib/Socket/Client/websocket.js +53 -0
  79. package/lib/Socket/business.d.ts +537 -0
  80. package/lib/Socket/business.js +421 -0
  81. package/lib/Socket/chats.d.ts +110 -0
  82. package/lib/Socket/chats.js +1006 -0
  83. package/lib/Socket/communities.d.ts +646 -0
  84. package/lib/Socket/communities.js +430 -0
  85. package/lib/Socket/engine.d.ts +129 -0
  86. package/lib/Socket/engine.js +767 -0
  87. package/lib/Socket/groups.d.ts +149 -0
  88. package/lib/Socket/groups.js +326 -0
  89. package/lib/Socket/index.d.ts +637 -0
  90. package/lib/Socket/index.js +14 -0
  91. package/lib/Socket/messages-recv.d.ts +239 -0
  92. package/lib/Socket/messages-recv.js +1307 -0
  93. package/lib/Socket/messages-send.d.ts +580 -0
  94. package/lib/Socket/messages-send.js +1639 -0
  95. package/lib/Socket/mex.d.ts +2 -0
  96. package/lib/Socket/mex.js +47 -0
  97. package/lib/Socket/newsletter.d.ts +176 -0
  98. package/lib/Socket/newsletter.js +456 -0
  99. package/lib/Socket/socket.d.ts +56 -0
  100. package/lib/Socket/socket.js +998 -0
  101. package/lib/Types/Auth.d.ts +115 -0
  102. package/lib/Types/Auth.js +1 -0
  103. package/lib/Types/Bussines.d.ts +36 -0
  104. package/lib/Types/Bussines.js +1 -0
  105. package/lib/Types/Call.d.ts +14 -0
  106. package/lib/Types/Call.js +1 -0
  107. package/lib/Types/Chat.d.ts +124 -0
  108. package/lib/Types/Chat.js +7 -0
  109. package/lib/Types/Contact.d.ts +23 -0
  110. package/lib/Types/Contact.js +1 -0
  111. package/lib/Types/Events.d.ts +241 -0
  112. package/lib/Types/Events.js +1 -0
  113. package/lib/Types/GroupMetadata.d.ts +67 -0
  114. package/lib/Types/GroupMetadata.js +1 -0
  115. package/lib/Types/Label.d.ts +46 -0
  116. package/lib/Types/Label.js +24 -0
  117. package/lib/Types/LabelAssociation.d.ts +29 -0
  118. package/lib/Types/LabelAssociation.js +6 -0
  119. package/lib/Types/Message.d.ts +427 -0
  120. package/lib/Types/Message.js +10 -0
  121. package/lib/Types/Newsletter.d.ts +134 -0
  122. package/lib/Types/Newsletter.js +30 -0
  123. package/lib/Types/Product.d.ts +78 -0
  124. package/lib/Types/Product.js +1 -0
  125. package/lib/Types/Signal.d.ts +77 -0
  126. package/lib/Types/Signal.js +1 -0
  127. package/lib/Types/Socket.d.ts +173 -0
  128. package/lib/Types/Socket.js +2 -0
  129. package/lib/Types/State.d.ts +38 -0
  130. package/lib/Types/State.js +12 -0
  131. package/lib/Types/USync.d.ts +25 -0
  132. package/lib/Types/USync.js +1 -0
  133. package/lib/Types/index.d.ts +75 -0
  134. package/lib/Types/index.js +32 -0
  135. package/lib/Utils/auth-utils.d.ts +18 -0
  136. package/lib/Utils/auth-utils.js +289 -0
  137. package/lib/Utils/browser-utils.d.ts +3 -0
  138. package/lib/Utils/browser-utils.js +47 -0
  139. package/lib/Utils/business.d.ts +22 -0
  140. package/lib/Utils/business.js +230 -0
  141. package/lib/Utils/chat-utils.d.ts +69 -0
  142. package/lib/Utils/chat-utils.js +811 -0
  143. package/lib/Utils/companion-reg-client-utils.d.ts +20 -0
  144. package/lib/Utils/companion-reg-client-utils.js +66 -0
  145. package/lib/Utils/crypto.d.ts +41 -0
  146. package/lib/Utils/crypto.js +117 -0
  147. package/lib/Utils/decode-wa-message.d.ts +47 -0
  148. package/lib/Utils/decode-wa-message.js +282 -0
  149. package/lib/Utils/event-buffer.d.ts +35 -0
  150. package/lib/Utils/event-buffer.js +590 -0
  151. package/lib/Utils/generics.d.ts +90 -0
  152. package/lib/Utils/generics.js +384 -0
  153. package/lib/Utils/history.d.ts +21 -0
  154. package/lib/Utils/history.js +130 -0
  155. package/lib/Utils/identity-change-handler.d.ts +36 -0
  156. package/lib/Utils/identity-change-handler.js +48 -0
  157. package/lib/Utils/index.d.ts +25 -0
  158. package/lib/Utils/index.js +24 -0
  159. package/lib/Utils/link-preview.d.ts +20 -0
  160. package/lib/Utils/link-preview.js +84 -0
  161. package/lib/Utils/logger.d.ts +12 -0
  162. package/lib/Utils/logger.js +2 -0
  163. package/lib/Utils/lt-hash.d.ts +10 -0
  164. package/lib/Utils/lt-hash.js +7 -0
  165. package/lib/Utils/make-mutex.d.ts +8 -0
  166. package/lib/Utils/make-mutex.js +32 -0
  167. package/lib/Utils/message-retry-manager.d.ts +109 -0
  168. package/lib/Utils/message-retry-manager.js +224 -0
  169. package/lib/Utils/messages-media.d.ts +129 -0
  170. package/lib/Utils/messages-media.js +784 -0
  171. package/lib/Utils/messages.d.ts +90 -0
  172. package/lib/Utils/messages.js +1240 -0
  173. package/lib/Utils/noise-handler.d.ts +19 -0
  174. package/lib/Utils/noise-handler.js +200 -0
  175. package/lib/Utils/offline-node-processor.d.ts +16 -0
  176. package/lib/Utils/offline-node-processor.js +39 -0
  177. package/lib/Utils/platform-identity.d.ts +14 -0
  178. package/lib/Utils/platform-identity.js +148 -0
  179. package/lib/Utils/pre-key-manager.d.ts +27 -0
  180. package/lib/Utils/pre-key-manager.js +105 -0
  181. package/lib/Utils/process-message.d.ts +59 -0
  182. package/lib/Utils/process-message.js +544 -0
  183. package/lib/Utils/reporting-utils.d.ts +10 -0
  184. package/lib/Utils/reporting-utils.js +257 -0
  185. package/lib/Utils/rich-messages.d.ts +276 -0
  186. package/lib/Utils/rich-messages.js +1143 -0
  187. package/lib/Utils/signal.d.ts +33 -0
  188. package/lib/Utils/signal.js +158 -0
  189. package/lib/Utils/stanza-ack.d.ts +10 -0
  190. package/lib/Utils/stanza-ack.js +37 -0
  191. package/lib/Utils/sticker-pack.d.ts +10 -0
  192. package/lib/Utils/sticker-pack.js +111 -0
  193. package/lib/Utils/sync-action-utils.d.ts +18 -0
  194. package/lib/Utils/sync-action-utils.js +47 -0
  195. package/lib/Utils/tc-token-utils.d.ts +8 -0
  196. package/lib/Utils/tc-token-utils.js +45 -0
  197. package/lib/Utils/use-multi-file-auth-state.d.ts +12 -0
  198. package/lib/Utils/use-multi-file-auth-state.js +120 -0
  199. package/lib/Utils/validate-connection.d.ts +10 -0
  200. package/lib/Utils/validate-connection.js +222 -0
  201. package/lib/VoIP/audio-feeder.d.ts +15 -0
  202. package/lib/VoIP/audio-feeder.js +136 -0
  203. package/lib/VoIP/index.d.ts +12 -0
  204. package/lib/VoIP/index.js +282 -0
  205. package/lib/VoIP/relay-transport.d.ts +43 -0
  206. package/lib/VoIP/relay-transport.js +559 -0
  207. package/lib/VoIP/signaling.js +603 -0
  208. package/lib/VoIP/types.d.ts +69 -0
  209. package/lib/VoIP/types.js +17 -0
  210. package/lib/VoIP/wasm-engine.d.ts +103 -0
  211. package/lib/VoIP/wasm-engine.js +1214 -0
  212. package/lib/VoIP/worker-bootstrap.js +1042 -0
  213. package/lib/WABinary/constants.d.ts +27 -0
  214. package/lib/WABinary/constants.js +1300 -0
  215. package/lib/WABinary/decode.d.ts +6 -0
  216. package/lib/WABinary/decode.js +261 -0
  217. package/lib/WABinary/encode.d.ts +2 -0
  218. package/lib/WABinary/encode.js +219 -0
  219. package/lib/WABinary/generic-utils.d.ts +16 -0
  220. package/lib/WABinary/generic-utils.js +190 -0
  221. package/lib/WABinary/index.d.ts +5 -0
  222. package/lib/WABinary/index.js +5 -0
  223. package/lib/WABinary/jid-utils.d.ts +47 -0
  224. package/lib/WABinary/jid-utils.js +95 -0
  225. package/lib/WABinary/types.d.ts +18 -0
  226. package/lib/WABinary/types.js +1 -0
  227. package/lib/WAM/BinaryInfo.d.ts +8 -0
  228. package/lib/WAM/BinaryInfo.js +9 -0
  229. package/lib/WAM/constants.d.ts +39 -0
  230. package/lib/WAM/constants.js +22852 -0
  231. package/lib/WAM/encode.d.ts +2 -0
  232. package/lib/WAM/encode.js +149 -0
  233. package/lib/WAM/index.d.ts +3 -0
  234. package/lib/WAM/index.js +3 -0
  235. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -0
  236. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -0
  237. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
  238. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -0
  239. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
  240. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
  241. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
  242. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
  243. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +25 -0
  244. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -0
  245. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +9 -0
  246. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
  247. package/lib/WAUSync/Protocols/index.d.ts +4 -0
  248. package/lib/WAUSync/Protocols/index.js +4 -0
  249. package/lib/WAUSync/USyncQuery.d.ts +28 -0
  250. package/lib/WAUSync/USyncQuery.js +93 -0
  251. package/lib/WAUSync/USyncUser.d.ts +12 -0
  252. package/lib/WAUSync/USyncUser.js +22 -0
  253. package/lib/WAUSync/index.d.ts +3 -0
  254. package/lib/WAUSync/index.js +3 -0
  255. package/lib/assets/wasm/loader.js +5 -0
  256. package/lib/assets/wasm/whatsapp.wasm +0 -0
  257. package/lib/assets/wasm/worker-modules.js +273 -0
  258. package/lib/index.d.ts +21 -0
  259. package/lib/index.js +22 -0
  260. package/package.json +111 -0
@@ -0,0 +1,60 @@
1
+ import type { WABrowserDescription } from '../Types/index.js';
2
+
3
+ export interface IdentityProfile { id: string; browser: WABrowserDescription; score: number; note: string }
4
+ export declare const IDENTITY_PROFILES: readonly IdentityProfile[];
5
+ export declare const IDENTITY_STATE_FILE: string;
6
+ export declare function defaultVersionForOs(os: string | undefined): string;
7
+
8
+ /** 'Mac OS/Chrome/15.6.1' | 'archLinux:Firefox' | 'linux-chrome' | tuple → tuple, atau null */
9
+ export declare function parseBrowserSpec(spec: string | readonly string[] | null | undefined): WABrowserDescription | null;
10
+ /** BIBZ_BROWSER / BIBZ_IDENTITY / BIBZ_DEVICE_OS / BIBZ_DEVICE_BROWSER / BIBZ_DEVICE_VERSION */
11
+ export declare function identityFromEnv(env?: NodeJS.ProcessEnv): WABrowserDescription | 'auto' | null;
12
+
13
+ export interface ResolvedIdentity {
14
+ mode: 'custom' | 'auto';
15
+ browser: WABrowserDescription;
16
+ source: string;
17
+ profileId?: string;
18
+ candidates: Array<{ id: string; browser: WABrowserDescription }>;
19
+ notes: Array<{ level: 'warn' | 'info'; message: string }>;
20
+ }
21
+ export declare function resolveDeviceIdentity(opts?: {
22
+ identity?: 'auto' | string | readonly string[] | null;
23
+ browser?: readonly string[] | null;
24
+ authDir?: string;
25
+ env?: NodeJS.ProcessEnv;
26
+ prefer?: string;
27
+ }): ResolvedIdentity;
28
+
29
+ export declare function identityStatePath(authDir: string): string;
30
+ export declare function loadIdentityState(authDir: string): { browser: WABrowserDescription; profileId?: string; mode?: string; reason?: string; at?: string } | null;
31
+ export declare function saveIdentityState(authDir: string, state: { browser: readonly string[]; profileId?: string; mode?: string; reason?: string }): boolean;
32
+ export declare function clearIdentityState(authDir: string): void;
33
+
34
+ export declare function isIdentityRejection(info?: { status?: number; sawQr?: boolean; phase?: 'connect' | 'pairing'; errorData?: unknown }): boolean;
35
+
36
+ export interface IdentityRotator {
37
+ readonly current: { id: string; browser: WABrowserDescription };
38
+ readonly index: number;
39
+ readonly tried: Array<{ id: string; reason: string }>;
40
+ readonly exhausted: boolean;
41
+ markStable(reason?: string): void;
42
+ next(reason: string): boolean;
43
+ }
44
+ export declare function createIdentityRotator(opts: {
45
+ candidates: Array<{ id: string; browser: WABrowserDescription }>;
46
+ authDir?: string;
47
+ mode: 'custom' | 'auto';
48
+ logger?: { warn?: (msg: string) => void };
49
+ onChange?: (candidate: { id: string; browser: WABrowserDescription }, reason: string) => void;
50
+ }): IdentityRotator;
51
+
52
+ export interface IdentityDescription {
53
+ browser: WABrowserDescription;
54
+ /** teks yang muncul di daftar Perangkat tertaut, mis. "Chrome (Arch Linux)" */
55
+ linkedDeviceName: string;
56
+ /** companion_platform_display yang dipakai saat pairing kode */
57
+ pairingDisplay: string;
58
+ pairingDisplayAccepted: boolean;
59
+ }
60
+ export declare function describeIdentity(browser: readonly string[], opts?: { companionPlatformDisplay?: string | null }): IdentityDescription;
@@ -0,0 +1,234 @@
1
+ // BibzWhats — identitas perangkat tertaut: KUSTOM atau OTOMATIS (paling stabil).
2
+ //
3
+ // Prinsip:
4
+ // • Kalau pemakai MENENTUKAN identitas (opsi `browser`/`identity`, atau env
5
+ // BIBZ_BROWSER / BIBZ_DEVICE_OS / BIBZ_DEVICE_BROWSER / BIBZ_DEVICE_VERSION),
6
+ // itu yang dipakai — apa adanya, tanpa "dibetulkan" diam-diam. Library hanya
7
+ // memberi peringatan bila kombinasi itu diketahui bermasalah.
8
+ // • Kalau TIDAK ditentukan (`identity: 'auto'`), library memilih profil dengan
9
+ // skor stabilitas tertinggi dari tabel di bawah (hasil pengukuran ke server
10
+ // WA 2026-09-03), dan bila server menolaknya di tengah jalan (428 sebelum QR,
11
+ // 400 saat pairing kode, 405 versi/klien ditolak), otomatis berpindah ke
12
+ // profil stabil berikutnya. Profil yang berhasil dipakai disimpan di
13
+ // <authDir>/identity.json agar konsisten antar restart — identitas harus
14
+ // tetap sama selama sesi hidup, kalau tidak WhatsApp menampilkan perangkat
15
+ // dengan nama berbeda dan bisa memutus sesi.
16
+
17
+ import fs from 'fs';
18
+ import path from 'path';
19
+ import { Browsers } from '../Utils/browser-utils.js';
20
+ import { derivePairingDisplay, isPairingDisplayAccepted, resolvePairingOs, hostOsLabel, hostOsVersion } from '../Utils/platform-identity.js';
21
+
22
+ /**
23
+ * Profil identitas, diurutkan dari yang PALING stabil.
24
+ * skor = gabungan: QR/handshake OK, pairing kode diterima tanpa penyesuaian
25
+ * display, tanpa webSubPlatform khusus, umum dipakai klien nyata (kecil
26
+ * kemungkinan dianggap anomali).
27
+ */
28
+ export const IDENTITY_PROFILES = Object.freeze([
29
+ { id: 'macos-chrome', browser: Browsers.macOS('Chrome'), score: 100, note: 'default; display "Chrome (Mac OS)" diterima apa adanya' },
30
+ { id: 'macos-safari', browser: Browsers.macOS('Safari'), score: 95, note: 'display "Safari (Mac OS)" diterima' },
31
+ { id: 'windows-chrome', browser: Browsers.windows('Chrome'), score: 92, note: 'display "Chrome (Windows)" diterima' },
32
+ { id: 'linux-chrome', browser: Browsers.linux('Chrome'), score: 90, note: 'display "Chrome (Linux)" diterima' },
33
+ { id: 'ubuntu-chrome', browser: Browsers.ubuntu('Chrome'), score: 88, note: 'display "Chrome (Ubuntu)" diterima' },
34
+ { id: 'macos-firefox', browser: Browsers.macOS('Firefox'), score: 85, note: 'display "Firefox (Mac OS)" diterima' },
35
+ { id: 'windows-edge', browser: Browsers.windows('Edge'), score: 84, note: 'display "Edge (Windows)" diterima' },
36
+ { id: 'archlinux-chrome', browser: Browsers.archLinux('Chrome'), score: 80, note: 'nama "Arch Linux" di HP; display diturunkan ke "Chrome (Linux)"' },
37
+ { id: 'macos-desktop', browser: Browsers.macOS('Desktop'), score: 60, note: 'riwayat penuh; webSubPlatform WEB_BROWSER (DARWIN ditutup server)' },
38
+ { id: 'windows-desktop', browser: Browsers.windows('Desktop'), score: 58, note: 'riwayat penuh; WIN_HYBRID' },
39
+ ]);
40
+
41
+ export const IDENTITY_STATE_FILE = 'identity.json';
42
+
43
+ const isTuple = (b) => Array.isArray(b) && b.length >= 2 && b.slice(0, 2).every((x) => typeof x === 'string' && x.trim());
44
+
45
+ /** versi default yang wajar bila pemakai tidak mengisi browser[2] */
46
+ export function defaultVersionForOs(os) {
47
+ const l = String(os || '').toLowerCase();
48
+ const preset = Object.values(Browsers).map((fn) => { try { return fn('Chrome'); } catch { return null; } })
49
+ .find((t) => t && t[0].toLowerCase() === l && t[2]);
50
+ if (preset) return preset[2];
51
+ if (/mac|darwin/.test(l)) return '15.6.1';
52
+ if (/windows/.test(l)) return '10.0.22631';
53
+ if (/linux|ubuntu|debian|arch|fedora/.test(l)) return '6.12.44';
54
+ return '1.0.0';
55
+ }
56
+ const withVersion = (t) => [t[0], t[1], t[2] && String(t[2]).trim() ? String(t[2]).trim() : defaultVersionForOs(t[0])];
57
+
58
+ /** 'Mac OS/Chrome/15.6.1' | 'archLinux:Firefox' | 'Arch Linux, Chrome' | 'macos-safari' → tuple */
59
+ export function parseBrowserSpec(spec) {
60
+ if (isTuple(spec)) return withVersion([spec[0], spec[1], spec[2]]);
61
+ if (typeof spec !== 'string' || !spec.trim()) return null;
62
+ const s = spec.trim();
63
+ const profile = IDENTITY_PROFILES.find((p) => p.id === s.toLowerCase());
64
+ if (profile) return [...profile.browser];
65
+ // preset:Browser (macOS:Chrome, archLinux:Firefox, windows:Edge, appropriate:Chrome)
66
+ const m = /^([A-Za-z]+)\s*[:]\s*([A-Za-z][\w ]*)$/.exec(s);
67
+ if (m) {
68
+ const key = Object.keys(Browsers).find((k) => k.toLowerCase() === m[1].toLowerCase());
69
+ if (key) return withVersion([...Browsers[key](m[2].trim())]);
70
+ }
71
+ const parts = s.split(/\s*[\/|,]\s*/);
72
+ if (parts.length >= 2) return withVersion([parts[0], parts[1], parts[2]]);
73
+ return null;
74
+ }
75
+
76
+ /** Baca identitas dari environment (semua opsional). BIBZ_BROWSER menang atas trio OS/BROWSER/VERSION. */
77
+ export function identityFromEnv(env = process.env) {
78
+ const raw = env.BIBZ_BROWSER || env.BIBZ_IDENTITY;
79
+ if (raw) {
80
+ if (/^auto$/i.test(raw.trim())) return 'auto';
81
+ const t = parseBrowserSpec(raw);
82
+ if (t) return t;
83
+ }
84
+ if (env.BIBZ_DEVICE_OS || env.BIBZ_DEVICE_BROWSER) {
85
+ const os = env.BIBZ_DEVICE_OS || hostOsLabel();
86
+ const browser = env.BIBZ_DEVICE_BROWSER || 'Chrome';
87
+ const version = env.BIBZ_DEVICE_VERSION || (env.BIBZ_DEVICE_OS ? '' : hostOsVersion());
88
+ return withVersion([os, browser, version]);
89
+ }
90
+ return null;
91
+ }
92
+
93
+ /**
94
+ * Tentukan identitas efektif.
95
+ * Prioritas: opsi `identity`/`browser` eksplisit → env → 'auto'.
96
+ * @returns {{ mode:'custom'|'auto', browser:[string,string,string], source:string, profileId?:string,
97
+ * candidates:Array<{id:string,browser:[string,string,string]}>, notes:Array<{level:string,message:string}> }}
98
+ */
99
+ export function resolveDeviceIdentity({ identity, browser, authDir, env = process.env, prefer } = {}) {
100
+ const notes = [];
101
+ const explicit = identity ?? browser;
102
+ // 1) eksplisit dari kode
103
+ if (explicit && explicit !== 'auto') {
104
+ const t = parseBrowserSpec(explicit);
105
+ if (t) {
106
+ notes.push(...customIdentityNotes(t));
107
+ return { mode: 'custom', browser: t, source: 'options', candidates: [{ id: 'custom', browser: t }], notes };
108
+ }
109
+ notes.push({ level: 'warn', message: `identitas '${String(explicit)}' tidak bisa dibaca — pakai mode otomatis` });
110
+ }
111
+ // 2) env
112
+ const fromEnv = identityFromEnv(env);
113
+ if (fromEnv && fromEnv !== 'auto') {
114
+ notes.push(...customIdentityNotes(fromEnv));
115
+ return { mode: 'custom', browser: fromEnv, source: 'env', candidates: [{ id: 'custom', browser: fromEnv }], notes };
116
+ }
117
+ // 3) otomatis: kandidat terurut; profil yang pernah sukses di sesi ini didahulukan
118
+ let candidates = [...IDENTITY_PROFILES]
119
+ .filter((p) => !/desktop$/.test(p.id)) // Desktop hanya bila diminta eksplisit (butuh syncFullHistory)
120
+ .sort((a, b) => b.score - a.score)
121
+ .map((p) => ({ id: p.id, browser: [...p.browser] }));
122
+ if (typeof prefer === 'string') {
123
+ const i = candidates.findIndex((c) => c.id === prefer);
124
+ if (i > 0) candidates = [candidates[i], ...candidates.slice(0, i), ...candidates.slice(i + 1)];
125
+ }
126
+ const saved = authDir ? loadIdentityState(authDir) : null;
127
+ if (saved?.browser && isTuple(saved.browser)) {
128
+ const i = candidates.findIndex((c) => c.id === saved.profileId);
129
+ if (i > 0) candidates = [candidates[i], ...candidates.slice(0, i), ...candidates.slice(i + 1)];
130
+ else if (i < 0) candidates = [{ id: saved.profileId || 'saved', browser: saved.browser }, ...candidates];
131
+ notes.push({ level: 'info', message: `identitas otomatis dilanjutkan dari sesi: ${saved.browser[1]} (${saved.browser[0]})` });
132
+ }
133
+ const first = candidates[0];
134
+ return { mode: 'auto', browser: first.browser, source: saved ? 'auto+saved' : 'auto', profileId: first.id, candidates, notes };
135
+ }
136
+
137
+ function customIdentityNotes(t) {
138
+ const notes = [];
139
+ if (!resolvePairingOs(t[0])) {
140
+ notes.push({ level: 'info', message: `OS '${t[0]}' tidak ada di allow-list pairing kode WhatsApp → display pairing otomatis '${derivePairingDisplay(t)}' (nama di daftar perangkat tetap '${t[0]}')` });
141
+ }
142
+ if (t[1] === 'Desktop') {
143
+ notes.push({ level: 'info', message: `identitas Desktop: riwayat penuh hanya diminta bila socketConfig.syncFullHistory=true; pairing kode memakai '${derivePairingDisplay(t)}'` });
144
+ }
145
+ return notes;
146
+ }
147
+
148
+ export function identityStatePath(authDir) {
149
+ return path.join(authDir, IDENTITY_STATE_FILE);
150
+ }
151
+
152
+ export function loadIdentityState(authDir) {
153
+ try {
154
+ const raw = fs.readFileSync(identityStatePath(authDir), 'utf8');
155
+ const j = JSON.parse(raw);
156
+ return j && isTuple(j.browser) ? j : null;
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ export function saveIdentityState(authDir, { browser, profileId, mode, reason }) {
163
+ try {
164
+ fs.mkdirSync(authDir, { recursive: true });
165
+ fs.writeFileSync(identityStatePath(authDir), JSON.stringify({ browser, profileId, mode, reason, at: new Date().toISOString() }, null, 2));
166
+ return true;
167
+ } catch {
168
+ return false;
169
+ }
170
+ }
171
+
172
+ export function clearIdentityState(authDir) {
173
+ try { fs.rmSync(identityStatePath(authDir), { force: true }); } catch {}
174
+ }
175
+
176
+ /**
177
+ * Apakah kegagalan ini menandakan server MENOLAK IDENTITAS (bukan jaringan/nomor)?
178
+ * - 428 sebelum QR pernah muncul: handshake/ClientPayload ditolak (mis. webSubPlatform ditutup)
179
+ * - 400 bad-request pada pairing kode: companion_platform_display tidak dikenal
180
+ * - 405 (Method Not Allowed) pada connect: klien/versi ditolak
181
+ * 401/408/429/515 BUKAN alasan ganti identitas.
182
+ */
183
+ export function isIdentityRejection({ status, sawQr = false, phase = 'connect', errorData } = {}) {
184
+ if (phase === 'pairing') return status === 400 || errorData === 400;
185
+ if (status === 405) return true;
186
+ if (status === 428 && !sawQr) return true;
187
+ return false;
188
+ }
189
+
190
+ /**
191
+ * Pengelola rotasi identitas untuk mode otomatis.
192
+ * next(reason) → kandidat berikutnya (atau null bila habis); current → kandidat aktif.
193
+ */
194
+ export function createIdentityRotator({ candidates, authDir, mode, logger, onChange = () => {} }) {
195
+ let index = 0;
196
+ const tried = [];
197
+ const current = () => candidates[index];
198
+ const commit = (reason) => {
199
+ if (authDir) saveIdentityState(authDir, { browser: current().browser, profileId: current().id, mode, reason });
200
+ };
201
+ return {
202
+ get current() { return current(); },
203
+ get index() { return index; },
204
+ get tried() { return [...tried]; },
205
+ get exhausted() { return index >= candidates.length - 1; },
206
+ /** panggil saat identitas terbukti bekerja (QR diterima / pairing diakui / open) */
207
+ markStable(reason = 'ok') {
208
+ commit(reason);
209
+ },
210
+ /** ganti ke kandidat berikutnya; false bila mode custom atau kandidat habis */
211
+ next(reason) {
212
+ if (mode !== 'auto') return false;
213
+ if (index >= candidates.length - 1) return false;
214
+ tried.push({ id: current().id, reason });
215
+ index += 1;
216
+ const c = current();
217
+ logger?.warn?.(`BibzWhats: identitas '${tried[tried.length - 1].id}' ditolak server (${reason}) → mencoba '${c.id}' = ${c.browser[1]} (${c.browser[0]})`);
218
+ if (authDir) saveIdentityState(authDir, { browser: c.browser, profileId: c.id, mode, reason: `rotated: ${reason}` });
219
+ onChange(c, reason);
220
+ return true;
221
+ },
222
+ };
223
+ }
224
+
225
+ /** Ringkasan identitas untuk log/`client.identity` */
226
+ export function describeIdentity(browser, { companionPlatformDisplay } = {}) {
227
+ const display = companionPlatformDisplay || derivePairingDisplay(browser);
228
+ return {
229
+ browser: [...browser],
230
+ linkedDeviceName: `${browser[1]} (${browser[0]})`,
231
+ pairingDisplay: display,
232
+ pairingDisplayAccepted: isPairingDisplayAccepted(display),
233
+ };
234
+ }
@@ -0,0 +1,29 @@
1
+ import type { WAMessage, proto } from '../index.js';
2
+ export type ExtractedType = 'text' | 'image' | 'video' | 'audio' | 'sticker' | 'document' | 'reaction' | 'button' | 'poll' | 'other';
3
+ export interface ExtractedMessage {
4
+ type: ExtractedType;
5
+ text: string;
6
+ participant: string;
7
+ mentions?: string[];
8
+ quoted?: string;
9
+ quotedParticipant?: string;
10
+ quotedStanzaId?: string;
11
+ quotedMessage?: proto.IMessage | null;
12
+ imageMsg?: proto.Message.IImageMessage;
13
+ videoMsg?: proto.Message.IVideoMessage;
14
+ audioMsg?: proto.Message.IAudioMessage;
15
+ stickerMsg?: proto.Message.IStickerMessage;
16
+ documentMsg?: proto.Message.IDocumentMessage;
17
+ reactionMsg?: proto.Message.IReactionMessage;
18
+ fileName?: string;
19
+ mimetype?: string;
20
+ fileLength?: number;
21
+ buttonId?: string;
22
+ buttonText?: string;
23
+ pollName?: string;
24
+ pollOptions?: string[];
25
+ otherKind?: string;
26
+ }
27
+ export declare function unwrapMessage(message: proto.IMessage | null | undefined): proto.IMessage | null;
28
+ export declare function extractMessage(m: WAMessage): ExtractedMessage | null;
29
+ export declare function messageTimestampMs(m: WAMessage): number;
@@ -0,0 +1,115 @@
1
+ // BibzWhats — ekstraksi isi pesan masuk (WAMessage → objek sederhana).
2
+ // Unwrap otomatis: ephemeral, view-once (v1/v2/ext), document-with-caption, edited.
3
+
4
+ function quotedText(quotedMessage) {
5
+ if (!quotedMessage) return '';
6
+ const q = quotedMessage;
7
+ if (typeof q.conversation === 'string') return q.conversation;
8
+ if (q.extendedTextMessage) return q.extendedTextMessage.text || '';
9
+ if (q.imageMessage) return q.imageMessage.caption || '[gambar]';
10
+ if (q.videoMessage) return q.videoMessage.caption || '[video]';
11
+ if (q.audioMessage) return '[voice note]';
12
+ if (q.documentMessage) return q.documentMessage.caption || '[dokumen]';
13
+ return '';
14
+ }
15
+
16
+ /** Buka lapisan pembungkus pesan sampai konten sebenarnya. */
17
+ export function unwrapMessage(message) {
18
+ let msg = message;
19
+ if (msg?.editedMessage?.message) msg = msg.editedMessage.message;
20
+ else if (msg?.protocolMessage?.editedMessage?.message) msg = msg.protocolMessage.editedMessage.message;
21
+ for (let i = 0; i < 8 && msg; i++) {
22
+ const inner =
23
+ msg.ephemeralMessage?.message ||
24
+ msg.viewOnceMessage?.message ||
25
+ msg.viewOnceMessageV2?.message ||
26
+ msg.viewOnceMessageV2Extension?.message ||
27
+ msg.documentWithCaptionMessage?.message;
28
+ if (!inner) break;
29
+ msg = inner;
30
+ }
31
+ return msg || null;
32
+ }
33
+
34
+ const toNumber = (v) => (typeof v?.toNumber === 'function' ? v.toNumber() : Number(v || 0));
35
+
36
+ /**
37
+ * @typedef {object} ExtractedMessage
38
+ * @property {'text'|'image'|'video'|'audio'|'sticker'|'document'|'reaction'|'button'|'poll'|'other'} type
39
+ * @property {string} text
40
+ * @property {string} participant
41
+ * @property {string[]} [mentions]
42
+ * @property {string} [quoted]
43
+ */
44
+
45
+ /**
46
+ * @param {import('../Types/index.js').WAMessage} m
47
+ * @returns {ExtractedMessage|null}
48
+ */
49
+ export function extractMessage(m) {
50
+ const msg = unwrapMessage(m?.message);
51
+ if (!msg) return null;
52
+ const participant = m?.key?.participant || m?.participant || '';
53
+ const contextOf = (ci) => ({
54
+ mentions: ci?.mentionedJid || ci?.mentionedJids || [],
55
+ quoted: ci?.quotedMessage ? quotedText(ci.quotedMessage) : '',
56
+ quotedParticipant: ci?.participant || '',
57
+ quotedStanzaId: ci?.stanzaId || '',
58
+ quotedMessage: ci?.quotedMessage || null,
59
+ });
60
+
61
+ if (msg.reactionMessage) return { type: 'reaction', reactionMsg: msg.reactionMessage, text: '', participant };
62
+ if (msg.buttonsResponseMessage) {
63
+ return { type: 'button', buttonId: msg.buttonsResponseMessage.selectedButtonId || '', buttonText: msg.buttonsResponseMessage.selectedDisplayText || '', text: '', participant };
64
+ }
65
+ if (msg.templateButtonReplyMessage) {
66
+ return { type: 'button', buttonId: msg.templateButtonReplyMessage.selectedId || '', buttonText: msg.templateButtonReplyMessage.selectedDisplayText || '', text: '', participant };
67
+ }
68
+ if (msg.listResponseMessage) {
69
+ return { type: 'button', buttonId: msg.listResponseMessage?.singleSelectReply?.selectedRowId || '', buttonText: '', text: '', participant };
70
+ }
71
+ if (msg.interactiveResponseMessage) {
72
+ let buttonId = '';
73
+ try {
74
+ const raw = msg.interactiveResponseMessage.nativeFlowResponseMessage?.paramsJson;
75
+ buttonId = raw ? JSON.parse(raw)?.id || '' : '';
76
+ } catch {}
77
+ return { type: 'button', buttonId, buttonText: msg.interactiveResponseMessage.body?.text || '', text: '', participant };
78
+ }
79
+ if (msg.pollUpdateMessage) {
80
+ return { type: 'poll', pollName: msg.pollUpdateMessage.name || '', pollOptions: (msg.pollUpdateMessage.selectedOptions || []).map((o) => o.optionName), text: '', participant };
81
+ }
82
+ if (typeof msg.conversation === 'string') return { type: 'text', text: msg.conversation, participant };
83
+ if (msg.extendedTextMessage) {
84
+ return { type: 'text', text: msg.extendedTextMessage.text || '', ...contextOf(msg.extendedTextMessage.contextInfo), participant };
85
+ }
86
+ if (msg.imageMessage) {
87
+ return { type: 'image', text: msg.imageMessage.caption || '', imageMsg: msg.imageMessage, ...contextOf(msg.imageMessage.contextInfo), participant };
88
+ }
89
+ if (msg.videoMessage) {
90
+ return { type: 'video', text: msg.videoMessage.caption || '', videoMsg: msg.videoMessage, ...contextOf(msg.videoMessage.contextInfo), participant };
91
+ }
92
+ if (msg.audioMessage) return { type: 'audio', audioMsg: msg.audioMessage, text: '', participant };
93
+ if (msg.stickerMessage) return { type: 'sticker', stickerMsg: msg.stickerMessage, text: '', participant };
94
+ if (msg.documentMessage) {
95
+ const d = msg.documentMessage;
96
+ return {
97
+ type: 'document', text: d.caption || '', documentMsg: d, fileName: d.fileName || '', mimetype: d.mimetype || '',
98
+ fileLength: toNumber(d.fileLength), ...contextOf(d.contextInfo), participant,
99
+ };
100
+ }
101
+ if (msg.buttonsMessage) return { type: 'text', text: msg.buttonsMessage.text || '', participant };
102
+ if (msg.listMessage) return { type: 'text', text: msg.listMessage.description || '', participant };
103
+ return { type: 'other', otherKind: Object.keys(msg)[0] || 'unknown', participant };
104
+ }
105
+
106
+ /** Timestamp pesan dalam ms (0 bila tidak ada). */
107
+ export function messageTimestampMs(m) {
108
+ let value = m?.messageTimestamp ?? m?.key?.messageTimestamp;
109
+ if (value && typeof value === 'object') {
110
+ if (typeof value.toNumber === 'function') value = value.toNumber();
111
+ else if (Number.isFinite(value.low)) value = value.low + (Number(value.high) || 0) * 0x100000000;
112
+ }
113
+ const seconds = Number(value);
114
+ return Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : 0;
115
+ }
@@ -0,0 +1,7 @@
1
+ export * from './pairing.js';
2
+ export * from './client.js';
3
+ export * from './device-identity.js';
4
+ export * from './send.js';
5
+ export * from './extract.js';
6
+ export * from './jid.js';
7
+ export { BIBZWHATS_VERSION, BIBZWHATS_NAME, BIBZWHATS_PACKAGE, BIBZWHATS_UPSTREAM, BIBZWHATS_WA_WEB_VERSION } from './version.js';
@@ -0,0 +1,8 @@
1
+ // BibzWhats — lapisan API tingkat tinggi.
2
+ export * from './pairing.js';
3
+ export * from './client.js';
4
+ export * from './device-identity.js';
5
+ export * from './send.js';
6
+ export * from './extract.js';
7
+ export * from './jid.js';
8
+ export { BIBZWHATS_VERSION, BIBZWHATS_NAME, BIBZWHATS_PACKAGE, BIBZWHATS_UPSTREAM, BIBZWHATS_WA_WEB_VERSION } from './version.js';
@@ -0,0 +1,22 @@
1
+ export declare function digitsOf(jid: unknown): string;
2
+ export declare const isGroupJid: (jid: unknown) => boolean;
3
+ export declare const isLidJid: (jid: unknown) => boolean;
4
+ export declare const isPnJid: (jid: unknown) => boolean;
5
+ export declare const isNewsletterJid: (jid: unknown) => boolean;
6
+ export declare const isStatusJid: (jid: unknown) => boolean;
7
+ export declare function pnJid(digits: unknown): string;
8
+ export declare function lidJid(digits: unknown): string;
9
+ export declare function sameUser(a: unknown, b: unknown): boolean;
10
+ export declare function normalizeJid(jid: unknown): string;
11
+ export declare class LidMap {
12
+ lidToPn: Map<string, string>;
13
+ pnToLid: Map<string, string>;
14
+ set(lid: string, pn: string): void;
15
+ phoneOf(lid: string): string | null;
16
+ lidOf(pn: string): string | null;
17
+ canonical(jid: string): string;
18
+ variants(jid: string): string[];
19
+ learnFromMessage(m: { key?: { remoteJid?: string | null; remoteJidAlt?: string | null; participant?: string | null; participantAlt?: string | null } }): void;
20
+ toJSON(): Record<string, string>;
21
+ static fromJSON(obj?: Record<string, string>): LidMap;
22
+ }
@@ -0,0 +1,98 @@
1
+ // BibzWhats — utilitas JID (PN / LID / grup) tanpa dependensi luar.
2
+
3
+ const S_WHATSAPP_NET = '@s.whatsapp.net'; // (diekspor oleh WABinary)
4
+
5
+ export function digitsOf(jid) {
6
+ return String(jid || '').split('@')[0].split(':')[0].replace(/\D/g, '');
7
+ }
8
+ export const isGroupJid = (jid) => String(jid || '').endsWith('@g.us');
9
+ export const isLidJid = (jid) => String(jid || '').endsWith('@lid');
10
+ export const isPnJid = (jid) => String(jid || '').endsWith(S_WHATSAPP_NET);
11
+ export const isNewsletterJid = (jid) => String(jid || '').endsWith('@newsletter');
12
+ export const isStatusJid = (jid) => String(jid || '') === 'status@broadcast';
13
+
14
+ export function pnJid(digits) {
15
+ const value = digitsOf(digits);
16
+ return value ? `${value}${S_WHATSAPP_NET}` : '';
17
+ }
18
+ export function lidJid(digits) {
19
+ const value = digitsOf(digits);
20
+ return value ? `${value}@lid` : '';
21
+ }
22
+ export function sameUser(a, b) {
23
+ const left = digitsOf(a);
24
+ const right = digitsOf(b);
25
+ return !!left && !!right && left === right;
26
+ }
27
+
28
+ /** Buang suffix device (":12") dari JID user. */
29
+ export function normalizeJid(jid) {
30
+ const raw = String(jid || '');
31
+ if (!raw || isGroupJid(raw)) return raw;
32
+ const domain = raw.includes('@') ? raw.slice(raw.indexOf('@')) : '';
33
+ const digits = digitsOf(raw);
34
+ return digits ? `${digits}${domain}` : raw;
35
+ }
36
+
37
+ /**
38
+ * Peta LID ↔ nomor telepon sederhana (in-memory). Isi dari
39
+ * key.remoteJidAlt / participantAlt dan event contacts.upsert.
40
+ */
41
+ export class LidMap {
42
+ constructor() {
43
+ this.lidToPn = new Map();
44
+ this.pnToLid = new Map();
45
+ }
46
+ set(lid, pn) {
47
+ if (!lid || !pn) return;
48
+ if (isPnJid(String(lid)) && isLidJid(String(pn))) [lid, pn] = [pn, lid];
49
+ const l = digitsOf(lid);
50
+ const p = digitsOf(pn);
51
+ if (!l || !p || l === p) return;
52
+ this.lidToPn.set(l, p);
53
+ this.pnToLid.set(p, l);
54
+ }
55
+ phoneOf(lid) {
56
+ return this.lidToPn.get(digitsOf(lid)) || null;
57
+ }
58
+ lidOf(pn) {
59
+ return this.pnToLid.get(digitsOf(pn)) || null;
60
+ }
61
+ /** JID kanonik: LID → PN bila mapping diketahui. */
62
+ canonical(jid) {
63
+ const normalized = normalizeJid(jid);
64
+ if (!normalized || isGroupJid(normalized)) return normalized;
65
+ if (isLidJid(normalized)) {
66
+ const mapped = this.phoneOf(normalized);
67
+ if (mapped) return pnJid(mapped);
68
+ }
69
+ return normalized;
70
+ }
71
+ /** Semua bentuk JID yang merujuk user yang sama. */
72
+ variants(jid) {
73
+ const normalized = normalizeJid(jid);
74
+ if (!normalized || isGroupJid(normalized)) return normalized ? [normalized] : [];
75
+ const out = new Set([normalized]);
76
+ if (isLidJid(normalized)) {
77
+ const pn = this.phoneOf(normalized);
78
+ if (pn) out.add(pnJid(pn));
79
+ } else {
80
+ const lid = this.lidOf(normalized);
81
+ if (lid) out.add(lidJid(lid));
82
+ }
83
+ return [...out];
84
+ }
85
+ /** Serap mapping dari pesan masuk (remoteJidAlt/participantAlt). */
86
+ learnFromMessage(m) {
87
+ if (m?.key?.remoteJid && m.key.remoteJidAlt) this.set(m.key.remoteJid, m.key.remoteJidAlt);
88
+ if (m?.key?.participant && m.key.participantAlt) this.set(m.key.participant, m.key.participantAlt);
89
+ }
90
+ toJSON() {
91
+ return Object.fromEntries(this.lidToPn);
92
+ }
93
+ static fromJSON(obj = {}) {
94
+ const map = new LidMap();
95
+ for (const [lid, pn] of Object.entries(obj || {})) map.set(lid, pn);
96
+ return map;
97
+ }
98
+ }
@@ -0,0 +1,35 @@
1
+ export declare const PAIRING_REFRESH_MS: number;
2
+ export declare const PAIRING_BACKOFF_MAX_MS: number;
3
+ export declare function normalizePairingCode(value: unknown): string;
4
+ /** kode error server (error.data dari stanza <error code=...>) atau statusCode Boom non-500 */
5
+ export declare function pairingErrorCode(error: unknown): number | undefined;
6
+ export declare function isRateLimitError(error: unknown): boolean;
7
+ export declare function isRegistrationRejected(error: unknown): boolean;
8
+ export declare function isTimeoutError(error: unknown): boolean;
9
+ export declare function isCustomPairingError(error: unknown): boolean;
10
+ export interface PairingCodeMeta {
11
+ custom: boolean;
12
+ fallback: boolean;
13
+ }
14
+ export interface PairingController {
15
+ start(): Promise<string | null>;
16
+ trigger(): Promise<string | null>;
17
+ stop(): void;
18
+ isInFlight(): boolean;
19
+ isStopped(): boolean;
20
+ getState(): { fallbackUsed: boolean; timerActive: boolean; rateLimitDelay: number };
21
+ }
22
+ export interface PairingControllerOptions {
23
+ sock: { requestPairingCode(phone: string, code?: string): Promise<string>; authState?: { creds?: { registered?: boolean } } };
24
+ phone: string;
25
+ pairingCode?: string;
26
+ onCode?: (code: string, meta: PairingCodeMeta) => void;
27
+ logger?: { warn?: (...a: any[]) => void; error?: (...a: any[]) => void; info?: (...a: any[]) => void };
28
+ refreshMs?: number;
29
+ backoffMaxMs?: number;
30
+ setTimeoutFn?: typeof setTimeout;
31
+ clearTimeoutFn?: typeof clearTimeout;
32
+ /** 400 bad-request dsb.: kembalikan true bila diambil alih (controller berhenti) */
33
+ onRejected?: (error: unknown, info: { code: number; phone?: string }) => boolean;
34
+ }
35
+ export declare function createPairingController(options: PairingControllerOptions): PairingController;