@nerxa/socketon 0.0.0-stage → 1.21.1

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 (289) hide show
  1. package/EXPERIMENTAL.md +350 -0
  2. package/LICENSE +24 -0
  3. package/README.md +6317 -2
  4. package/WAProto/index.d.ts +17301 -0
  5. package/WAProto/index.js +125088 -0
  6. package/engine-requirements.js +14 -0
  7. package/package.json +125 -4
  8. package/src/Defaults/index.d.ts +152 -0
  9. package/src/Defaults/index.js +164 -0
  10. package/src/MessageBuilder/bot-signature.d.ts +22 -0
  11. package/src/MessageBuilder/bot-signature.js +122 -0
  12. package/src/MessageBuilder/extras.d.ts +449 -0
  13. package/src/MessageBuilder/extras.js +1356 -0
  14. package/src/MessageBuilder/index.d.ts +210 -0
  15. package/src/MessageBuilder/index.js +3594 -0
  16. package/src/MessageBuilder/metaai.d.ts +438 -0
  17. package/src/MessageBuilder/metaai.js +890 -0
  18. package/src/Signal/Group/ciphertext-message.d.ts +9 -0
  19. package/src/Signal/Group/ciphertext-message.js +11 -0
  20. package/src/Signal/Group/group-session-builder.d.ts +7 -0
  21. package/src/Signal/Group/group-session-builder.js +27 -0
  22. package/src/Signal/Group/group_cipher.d.ts +11 -0
  23. package/src/Signal/Group/group_cipher.js +78 -0
  24. package/src/Signal/Group/index.d.ts +11 -0
  25. package/src/Signal/Group/index.js +11 -0
  26. package/src/Signal/Group/keyhelper.d.ts +6 -0
  27. package/src/Signal/Group/keyhelper.js +17 -0
  28. package/src/Signal/Group/sender-chain-key.d.ts +13 -0
  29. package/src/Signal/Group/sender-chain-key.js +25 -0
  30. package/src/Signal/Group/sender-key-distribution-message.d.ts +16 -0
  31. package/src/Signal/Group/sender-key-distribution-message.js +62 -0
  32. package/src/Signal/Group/sender-key-message.d.ts +18 -0
  33. package/src/Signal/Group/sender-key-message.js +65 -0
  34. package/src/Signal/Group/sender-key-name.d.ts +11 -0
  35. package/src/Signal/Group/sender-key-name.js +47 -0
  36. package/src/Signal/Group/sender-key-record.d.ts +12 -0
  37. package/src/Signal/Group/sender-key-record.js +40 -0
  38. package/src/Signal/Group/sender-key-state.d.ts +16 -0
  39. package/src/Signal/Group/sender-key-state.js +83 -0
  40. package/src/Signal/Group/sender-message-key.d.ts +11 -0
  41. package/src/Signal/Group/sender-message-key.js +25 -0
  42. package/src/Signal/libsignal.d.ts +54 -0
  43. package/src/Signal/libsignal.js +427 -0
  44. package/src/Signal/lid-mapping.d.ts +20 -0
  45. package/src/Signal/lid-mapping.js +274 -0
  46. package/src/Socket/Client/index.d.ts +2 -0
  47. package/src/Socket/Client/index.js +3 -0
  48. package/src/Socket/Client/tcp.js +177 -0
  49. package/src/Socket/Client/types.d.ts +5 -0
  50. package/src/Socket/Client/types.js +9 -0
  51. package/src/Socket/Client/websocket.d.ts +12 -0
  52. package/src/Socket/Client/websocket.js +53 -0
  53. package/src/Socket/business.d.ts +431 -0
  54. package/src/Socket/business.js +379 -0
  55. package/src/Socket/chats.d.ts +247 -0
  56. package/src/Socket/chats.js +1484 -0
  57. package/src/Socket/communities.d.ts +598 -0
  58. package/src/Socket/communities.js +421 -0
  59. package/src/Socket/email.js +145 -0
  60. package/src/Socket/groups.d.ts +298 -0
  61. package/src/Socket/groups.js +339 -0
  62. package/src/Socket/index.d.ts +581 -0
  63. package/src/Socket/index.js +77 -0
  64. package/src/Socket/messages-recv.d.ts +370 -0
  65. package/src/Socket/messages-recv.js +1864 -0
  66. package/src/Socket/messages-send.d.ts +390 -0
  67. package/src/Socket/messages-send.js +1574 -0
  68. package/src/Socket/mex.d.ts +1 -0
  69. package/src/Socket/mex.js +44 -0
  70. package/src/Socket/mobile-builders.js +72 -0
  71. package/src/Socket/mobile.js +424 -0
  72. package/src/Socket/newsletter.d.ts +468 -0
  73. package/src/Socket/newsletter.js +671 -0
  74. package/src/Socket/socket.d.ts +77 -0
  75. package/src/Socket/socket.js +1106 -0
  76. package/src/Store/index.d.ts +3 -0
  77. package/src/Store/index.js +3 -0
  78. package/src/Store/make-in-memory-store.d.ts +62 -0
  79. package/src/Store/make-in-memory-store.js +396 -0
  80. package/src/Store/make-ordered-dictionary.d.ts +12 -0
  81. package/src/Store/make-ordered-dictionary.js +78 -0
  82. package/src/Store/object-repository.d.ts +10 -0
  83. package/src/Store/object-repository.js +23 -0
  84. package/src/Types/Auth.d.ts +1 -0
  85. package/src/Types/Auth.js +1 -0
  86. package/src/Types/Bussines.d.ts +1 -0
  87. package/src/Types/Bussines.js +1 -0
  88. package/src/Types/Call.d.ts +1 -0
  89. package/src/Types/Call.js +1 -0
  90. package/src/Types/Chat.d.ts +2 -0
  91. package/src/Types/Chat.js +24 -0
  92. package/src/Types/Contact.d.ts +1 -0
  93. package/src/Types/Contact.js +1 -0
  94. package/src/Types/Events.d.ts +1 -0
  95. package/src/Types/Events.js +0 -0
  96. package/src/Types/GroupMetadata.d.ts +5 -0
  97. package/src/Types/GroupMetadata.js +6 -0
  98. package/src/Types/Label.d.ts +2 -0
  99. package/src/Types/Label.js +24 -0
  100. package/src/Types/LabelAssociation.d.ts +2 -0
  101. package/src/Types/LabelAssociation.js +6 -0
  102. package/src/Types/Message.d.ts +13 -0
  103. package/src/Types/Message.js +23 -0
  104. package/src/Types/Mex.d.ts +2 -0
  105. package/src/Types/Mex.js +95 -0
  106. package/src/Types/Product.d.ts +1 -0
  107. package/src/Types/Product.js +1 -0
  108. package/src/Types/Signal.d.ts +1 -0
  109. package/src/Types/Signal.js +0 -0
  110. package/src/Types/Socket.d.ts +1 -0
  111. package/src/Types/Socket.js +0 -0
  112. package/src/Types/State.d.ts +5 -0
  113. package/src/Types/State.js +54 -0
  114. package/src/Types/USync.d.ts +1 -0
  115. package/src/Types/USync.js +0 -0
  116. package/src/Types/index.d.ts +13 -0
  117. package/src/Types/index.js +25 -0
  118. package/src/Utils/auth-store.d.ts +29 -0
  119. package/src/Utils/auth-store.js +76 -0
  120. package/src/Utils/auth-utils.d.ts +58 -0
  121. package/src/Utils/auth-utils.js +273 -0
  122. package/src/Utils/browser-utils.d.ts +9 -0
  123. package/src/Utils/browser-utils.js +28 -0
  124. package/src/Utils/business.d.ts +49 -0
  125. package/src/Utils/business.js +226 -0
  126. package/src/Utils/chat-utils.d.ts +408 -0
  127. package/src/Utils/chat-utils.js +846 -0
  128. package/src/Utils/companion-reg-client-utils.d.ts +4 -0
  129. package/src/Utils/companion-reg-client-utils.js +39 -0
  130. package/src/Utils/crypto.d.ts +39 -0
  131. package/src/Utils/crypto.js +110 -0
  132. package/src/Utils/decode-wa-message.d.ts +99 -0
  133. package/src/Utils/decode-wa-message.js +373 -0
  134. package/src/Utils/event-buffer.d.ts +22 -0
  135. package/src/Utils/event-buffer.js +624 -0
  136. package/src/Utils/generics.d.ts +63 -0
  137. package/src/Utils/generics.js +375 -0
  138. package/src/Utils/group-metadata-cache.d.ts +4 -0
  139. package/src/Utils/group-metadata-cache.js +25 -0
  140. package/src/Utils/history.d.ts +75 -0
  141. package/src/Utils/history.js +131 -0
  142. package/src/Utils/html-app.d.ts +19 -0
  143. package/src/Utils/html-app.js +96 -0
  144. package/src/Utils/html-multiplayer.d.ts +10 -0
  145. package/src/Utils/html-multiplayer.js +94 -0
  146. package/src/Utils/identity-change-handler.d.ts +13 -0
  147. package/src/Utils/identity-change-handler.js +48 -0
  148. package/src/Utils/index.d.ts +44 -0
  149. package/src/Utils/index.js +44 -0
  150. package/src/Utils/link-preview-metadata.d.ts +47 -0
  151. package/src/Utils/link-preview-metadata.js +88 -0
  152. package/src/Utils/link-preview.d.ts +12 -0
  153. package/src/Utils/link-preview.js +79 -0
  154. package/src/Utils/logger.d.ts +2 -0
  155. package/src/Utils/logger.js +2 -0
  156. package/src/Utils/lt-hash.d.ts +6 -0
  157. package/src/Utils/lt-hash.js +3 -0
  158. package/src/Utils/make-mutex.d.ts +6 -0
  159. package/src/Utils/make-mutex.js +32 -0
  160. package/src/Utils/message-retry-manager.d.ts +79 -0
  161. package/src/Utils/message-retry-manager.js +255 -0
  162. package/src/Utils/messages-media.d.ts +136 -0
  163. package/src/Utils/messages-media.js +814 -0
  164. package/src/Utils/messages.d.ts +62 -0
  165. package/src/Utils/messages.js +2223 -0
  166. package/src/Utils/mobile-companion-crypto.js +191 -0
  167. package/src/Utils/modern-messages.d.ts +150 -0
  168. package/src/Utils/modern-messages.js +496 -0
  169. package/src/Utils/native-flow.d.ts +11 -0
  170. package/src/Utils/native-flow.js +115 -0
  171. package/src/Utils/newsletter-status.d.ts +158 -0
  172. package/src/Utils/newsletter-status.js +651 -0
  173. package/src/Utils/noise-handler.d.ts +20 -0
  174. package/src/Utils/noise-handler.js +200 -0
  175. package/src/Utils/offline-node-processor.d.ts +9 -0
  176. package/src/Utils/offline-node-processor.js +33 -0
  177. package/src/Utils/optional-media.d.ts +3 -0
  178. package/src/Utils/optional-media.js +45 -0
  179. package/src/Utils/payment-messages.d.ts +66 -0
  180. package/src/Utils/payment-messages.js +160 -0
  181. package/src/Utils/pre-key-manager.d.ts +25 -0
  182. package/src/Utils/pre-key-manager.js +95 -0
  183. package/src/Utils/process-message.d.ts +60 -0
  184. package/src/Utils/process-message.js +688 -0
  185. package/src/Utils/reporting-utils.d.ts +12 -0
  186. package/src/Utils/reporting-utils.js +256 -0
  187. package/src/Utils/scheduled-message.d.ts +51 -0
  188. package/src/Utils/scheduled-message.js +112 -0
  189. package/src/Utils/sender-key-memory.d.ts +19 -0
  190. package/src/Utils/sender-key-memory.js +51 -0
  191. package/src/Utils/signal.d.ts +82 -0
  192. package/src/Utils/signal.js +193 -0
  193. package/src/Utils/stanza-ack.d.ts +16 -0
  194. package/src/Utils/stanza-ack.js +30 -0
  195. package/src/Utils/status-stickers.d.ts +127 -0
  196. package/src/Utils/status-stickers.js +260 -0
  197. package/src/Utils/sync-action-utils.d.ts +2 -0
  198. package/src/Utils/sync-action-utils.js +44 -0
  199. package/src/Utils/tc-token-utils.d.ts +29 -0
  200. package/src/Utils/tc-token-utils.js +155 -0
  201. package/src/Utils/ttl-cache.d.ts +28 -0
  202. package/src/Utils/ttl-cache.js +120 -0
  203. package/src/Utils/use-mongo-auth-state.d.ts +21 -0
  204. package/src/Utils/use-mongo-auth-state.js +67 -0
  205. package/src/Utils/use-multi-file-auth-state.d.ts +11 -0
  206. package/src/Utils/use-multi-file-auth-state.js +106 -0
  207. package/src/Utils/use-mysql-auth-state.d.ts +19 -0
  208. package/src/Utils/use-mysql-auth-state.js +79 -0
  209. package/src/Utils/use-postgres-auth-state.d.ts +19 -0
  210. package/src/Utils/use-postgres-auth-state.js +79 -0
  211. package/src/Utils/use-redis-auth-state.d.ts +19 -0
  212. package/src/Utils/use-redis-auth-state.js +89 -0
  213. package/src/Utils/use-single-file-auth-state.d.ts +10 -0
  214. package/src/Utils/use-single-file-auth-state.js +108 -0
  215. package/src/Utils/use-sqlite-auth-state.d.ts +12 -0
  216. package/src/Utils/use-sqlite-auth-state.js +120 -0
  217. package/src/Utils/useNekoDBAuth.d.ts +19 -0
  218. package/src/Utils/useNekoDBAuth.js +111 -0
  219. package/src/Utils/username.d.ts +12 -0
  220. package/src/Utils/username.js +58 -0
  221. package/src/Utils/validate-connection.d.ts +43 -0
  222. package/src/Utils/validate-connection.js +277 -0
  223. package/src/Utils/voice-recognition.d.ts +53 -0
  224. package/src/Utils/voice-recognition.js +219 -0
  225. package/src/Voip/audio-feeder.d.ts +24 -0
  226. package/src/Voip/audio-feeder.js +268 -0
  227. package/src/Voip/index.d.ts +88 -0
  228. package/src/Voip/index.js +936 -0
  229. package/src/Voip/relay-transport.d.ts +5 -0
  230. package/src/Voip/relay-transport.js +824 -0
  231. package/src/Voip/signaling.d.ts +11 -0
  232. package/src/Voip/signaling.js +1099 -0
  233. package/src/Voip/types.d.ts +2 -0
  234. package/src/Voip/types.js +12 -0
  235. package/src/Voip/video-feeder.d.ts +24 -0
  236. package/src/Voip/video-feeder.js +268 -0
  237. package/src/Voip/wasm-engine.d.ts +26 -0
  238. package/src/Voip/wasm-engine.js +1307 -0
  239. package/src/Voip/worker-bootstrap.js +1023 -0
  240. package/src/WABinary/constants.d.ts +22 -0
  241. package/src/WABinary/constants.js +1300 -0
  242. package/src/WABinary/decode.d.ts +9 -0
  243. package/src/WABinary/decode.js +261 -0
  244. package/src/WABinary/encode.d.ts +2 -0
  245. package/src/WABinary/encode.js +219 -0
  246. package/src/WABinary/generic-utils.d.ts +76 -0
  247. package/src/WABinary/generic-utils.js +214 -0
  248. package/src/WABinary/index.d.ts +5 -0
  249. package/src/WABinary/index.js +5 -0
  250. package/src/WABinary/jid-utils.d.ts +30 -0
  251. package/src/WABinary/jid-utils.js +97 -0
  252. package/src/WABinary/types.d.ts +1 -0
  253. package/src/WABinary/types.js +0 -0
  254. package/src/WAM/BinaryInfo.d.ts +7 -0
  255. package/src/WAM/BinaryInfo.js +9 -0
  256. package/src/WAM/constants.d.ts +34926 -0
  257. package/src/WAM/constants.js +22852 -0
  258. package/src/WAM/encode.d.ts +1 -0
  259. package/src/WAM/encode.js +148 -0
  260. package/src/WAM/index.d.ts +3 -0
  261. package/src/WAM/index.js +3 -0
  262. package/src/WAUSync/Protocols/USyncContactProtocol.d.ts +36 -0
  263. package/src/WAUSync/Protocols/USyncContactProtocol.js +50 -0
  264. package/src/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
  265. package/src/WAUSync/Protocols/USyncDeviceProtocol.js +51 -0
  266. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
  267. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
  268. package/src/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
  269. package/src/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
  270. package/src/WAUSync/Protocols/USyncUsernameProtocol.d.ts +9 -0
  271. package/src/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
  272. package/src/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +38 -0
  273. package/src/WAUSync/Protocols/UsyncBotProfileProtocol.js +49 -0
  274. package/src/WAUSync/Protocols/UsyncLIDProtocol.d.ts +14 -0
  275. package/src/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
  276. package/src/WAUSync/Protocols/index.d.ts +5 -0
  277. package/src/WAUSync/Protocols/index.js +5 -0
  278. package/src/WAUSync/USyncQuery.d.ts +20 -0
  279. package/src/WAUSync/USyncQuery.js +93 -0
  280. package/src/WAUSync/USyncUser.d.ts +16 -0
  281. package/src/WAUSync/USyncUser.js +30 -0
  282. package/src/WAUSync/index.d.ts +3 -0
  283. package/src/WAUSync/index.js +3 -0
  284. package/src/assets/wasm/README.md +76 -0
  285. package/src/assets/wasm/loader.js +5 -0
  286. package/src/assets/wasm/whatsapp.wasm +0 -0
  287. package/src/assets/wasm/worker-modules.js +273 -0
  288. package/src/index.d.ts +43 -0
  289. package/src/index.js +48 -0
@@ -0,0 +1,193 @@
1
+ import { KEY_BUNDLE_TYPE } from '../Defaults/index.js';
2
+ import { assertNodeErrorFree, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildUInt, getServerFromDomainType, jidDecode, S_WHATSAPP_NET, WAJIDDomains } from '../WABinary/index.js';
3
+ import { Curve, generateSignalPubKey } from './crypto.js';
4
+ import { encodeBigEndian } from './generics.js';
5
+ function chunk(array, size) {
6
+ const chunks = [];
7
+ for (let i = 0; i < array.length; i += size) {
8
+ chunks.push(array.slice(i, i + size));
9
+ }
10
+ return chunks;
11
+ }
12
+ export const createSignalIdentity = (wid, accountSignatureKey) => {
13
+ return {
14
+ identifier: { name: wid, deviceId: 0 },
15
+ identifierKey: generateSignalPubKey(accountSignatureKey)
16
+ };
17
+ };
18
+ export const getPreKeys = async ({ get }, min, limit) => {
19
+ const idList = [];
20
+ for (let id = min; id < limit; id++) {
21
+ idList.push(id.toString());
22
+ }
23
+ return get('pre-key', idList);
24
+ };
25
+ export const generateOrGetPreKeys = (creds, range) => {
26
+ const avaliable = creds.nextPreKeyId - creds.firstUnuploadedPreKeyId;
27
+ const remaining = range - avaliable;
28
+ const lastPreKeyId = creds.nextPreKeyId + remaining - 1;
29
+ const newPreKeys = {};
30
+ if (remaining > 0) {
31
+ for (let i = creds.nextPreKeyId; i <= lastPreKeyId; i++) {
32
+ newPreKeys[i] = Curve.generateKeyPair();
33
+ }
34
+ }
35
+ return {
36
+ newPreKeys,
37
+ lastPreKeyId,
38
+ preKeysRange: [creds.firstUnuploadedPreKeyId, range]
39
+ };
40
+ };
41
+ export const xmppSignedPreKey = (key) => ({
42
+ tag: 'skey',
43
+ attrs: {},
44
+ content: [
45
+ { tag: 'id', attrs: {}, content: encodeBigEndian(key.keyId, 3) },
46
+ { tag: 'value', attrs: {}, content: key.keyPair.public },
47
+ { tag: 'signature', attrs: {}, content: key.signature }
48
+ ]
49
+ });
50
+ export const xmppPreKey = (pair, id) => ({
51
+ tag: 'key',
52
+ attrs: {},
53
+ content: [
54
+ { tag: 'id', attrs: {}, content: encodeBigEndian(id, 3) },
55
+ { tag: 'value', attrs: {}, content: pair.public }
56
+ ]
57
+ });
58
+ const isValidUInt = (n) => typeof n === 'number' && Number.isInteger(n);
59
+ export const extractE2ESessionFromRetryReceipt = (receipt) => {
60
+ const keysNode = getBinaryNodeChild(receipt, 'keys');
61
+ if (!keysNode)
62
+ return null;
63
+ const typeBuf = getBinaryNodeChildBuffer(keysNode, 'type');
64
+ if (!typeBuf || typeBuf.length !== 1 || typeBuf[0] !== KEY_BUNDLE_TYPE[0])
65
+ return null;
66
+ const identity = getBinaryNodeChildBuffer(keysNode, 'identity');
67
+ const skey = getBinaryNodeChild(keysNode, 'skey');
68
+ if (!identity || identity.length !== 32 || !skey)
69
+ return null;
70
+ const registrationId = getBinaryNodeChildUInt(receipt, 'registration', 4);
71
+ if (!isValidUInt(registrationId))
72
+ return null;
73
+ const signedPubKey = getBinaryNodeChildBuffer(skey, 'value');
74
+ const signedSig = getBinaryNodeChildBuffer(skey, 'signature');
75
+ const signedKeyId = getBinaryNodeChildUInt(skey, 'id', 3);
76
+ if (!signedPubKey || signedPubKey.length !== 32 || !signedSig || !isValidUInt(signedKeyId)) {
77
+ return null;
78
+ }
79
+ const preKeyNode = getBinaryNodeChild(keysNode, 'key');
80
+ let preKey;
81
+ if (preKeyNode) {
82
+ const preKeyPub = getBinaryNodeChildBuffer(preKeyNode, 'value');
83
+ const preKeyId = getBinaryNodeChildUInt(preKeyNode, 'id', 3);
84
+ if (!preKeyPub || preKeyPub.length !== 32 || !isValidUInt(preKeyId)) {
85
+ return null;
86
+ }
87
+ preKey = { keyId: preKeyId, publicKey: generateSignalPubKey(preKeyPub) };
88
+ }
89
+ return {
90
+ registrationId,
91
+ identityKey: generateSignalPubKey(identity),
92
+ signedPreKey: {
93
+ keyId: signedKeyId,
94
+ publicKey: generateSignalPubKey(signedPubKey),
95
+ signature: signedSig
96
+ },
97
+ preKey
98
+ };
99
+ };
100
+ export const parseAndInjectE2ESessions = async (node, repository) => {
101
+ const extractKey = (key) => key
102
+ ? {
103
+ keyId: getBinaryNodeChildUInt(key, 'id', 3),
104
+ publicKey: generateSignalPubKey(getBinaryNodeChildBuffer(key, 'value')),
105
+ signature: getBinaryNodeChildBuffer(key, 'signature')
106
+ }
107
+ : undefined;
108
+ const nodes = getBinaryNodeChildren(getBinaryNodeChild(node, 'list'), 'user');
109
+ for (const node of nodes) {
110
+ assertNodeErrorFree(node);
111
+ }
112
+
113
+ const chunkSize = 100;
114
+ const chunks = chunk(nodes, chunkSize);
115
+ for (const nodesChunk of chunks) {
116
+ for (const node of nodesChunk) {
117
+ const signedKey = getBinaryNodeChild(node, 'skey');
118
+ const key = getBinaryNodeChild(node, 'key');
119
+ const identity = getBinaryNodeChildBuffer(node, 'identity');
120
+ const jid = node.attrs.jid;
121
+ const registrationId = getBinaryNodeChildUInt(node, 'registration', 4);
122
+ await repository.injectE2ESession({
123
+ jid,
124
+ session: {
125
+ registrationId: registrationId,
126
+ identityKey: generateSignalPubKey(identity),
127
+ signedPreKey: extractKey(signedKey),
128
+ preKey: extractKey(key)
129
+ }
130
+ });
131
+ }
132
+ }
133
+ };
134
+ export const extractDeviceJids = (result, myJid, myLid, excludeZeroDevices) => {
135
+ const { user: myUser, device: myDevice } = jidDecode(myJid);
136
+ const extracted = [];
137
+ for (const userResult of result) {
138
+ const { devices, id } = userResult;
139
+ const decoded = jidDecode(id), { user, server } = decoded;
140
+ let { domainType } = decoded;
141
+ const deviceList = devices?.deviceList;
142
+ if (!Array.isArray(deviceList))
143
+ continue;
144
+ for (const { id: device, keyIndex, isHosted } of deviceList) {
145
+ if ((!excludeZeroDevices || device !== 0) &&
146
+ ((myUser !== user && myLid !== user) || myDevice !== device) &&
147
+ (device === 0 || !!keyIndex)
148
+ ) {
149
+ if (isHosted) {
150
+ domainType = domainType === WAJIDDomains.LID ? WAJIDDomains.HOSTED_LID : WAJIDDomains.HOSTED;
151
+ }
152
+ extracted.push({
153
+ user,
154
+ device,
155
+ domainType,
156
+ server: getServerFromDomainType(server, domainType)
157
+ });
158
+ }
159
+ }
160
+ }
161
+ return extracted;
162
+ };
163
+
164
+ export const getNextPreKeys = async ({ creds, keys }, count) => {
165
+ const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(creds, count);
166
+ const update = {
167
+ nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId),
168
+ firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1)
169
+ };
170
+ await keys.set({ 'pre-key': newPreKeys });
171
+ const preKeys = await getPreKeys(keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1]);
172
+ return { update, preKeys };
173
+ };
174
+ export const getNextPreKeysNode = async (state, count) => {
175
+ const { creds } = state;
176
+ const { update, preKeys } = await getNextPreKeys(state, count);
177
+ const node = {
178
+ tag: 'iq',
179
+ attrs: {
180
+ xmlns: 'encrypt',
181
+ type: 'set',
182
+ to: S_WHATSAPP_NET
183
+ },
184
+ content: [
185
+ { tag: 'registration', attrs: {}, content: encodeBigEndian(creds.registrationId) },
186
+ { tag: 'type', attrs: {}, content: KEY_BUNDLE_TYPE },
187
+ { tag: 'identity', attrs: {}, content: creds.signedIdentityKey.public },
188
+ { tag: 'list', attrs: {}, content: Object.keys(preKeys).map(k => xmppPreKey(preKeys[+k], +k)) },
189
+ xmppSignedPreKey(creds.signedPreKey)
190
+ ]
191
+ };
192
+ return { update, node };
193
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Builds an ACK stanza for a received node.
3
+ * Pure function -- no I/O, no side effects.
4
+ *
5
+ * Mirrors WhatsApp Web's ACK construction:
6
+ * - WAWebHandleMsgSendAck.sendAck / sendNack
7
+ * - WAWebCreateNackFromStanza.createNackFromStanza
8
+ */
9
+ export function buildAckStanza(node: any, errorCode: any, meId: any): {
10
+ tag: string;
11
+ attrs: {
12
+ id: any;
13
+ to: any;
14
+ class: any;
15
+ };
16
+ };
@@ -0,0 +1,30 @@
1
+
2
+ export function buildAckStanza(node, errorCode, meId) {
3
+ const { tag, attrs } = node;
4
+ const stanza = {
5
+ tag: 'ack',
6
+ attrs: {
7
+ id: attrs.id,
8
+ to: attrs.from,
9
+ class: tag
10
+ }
11
+ };
12
+ if (errorCode) {
13
+ stanza.attrs.error = errorCode.toString();
14
+ }
15
+ if (attrs.participant) {
16
+ stanza.attrs.participant = attrs.participant;
17
+ }
18
+ if (attrs.recipient) {
19
+ stanza.attrs.recipient = attrs.recipient;
20
+ }
21
+
22
+ if (attrs.type) {
23
+ stanza.attrs.type = attrs.type;
24
+ }
25
+
26
+ if (tag === 'message' && meId) {
27
+ stanza.attrs.from = meId;
28
+ }
29
+ return stanza;
30
+ }
@@ -0,0 +1,127 @@
1
+ import type { proto } from '../../WAProto/index.js';
2
+
3
+ export declare const StatusLinkType: Readonly<{
4
+ RASTERIZED_LINK_PREVIEW: 1;
5
+ RASTERIZED_LINK_TRUNCATED: 2;
6
+ RASTERIZED_LINK_FULL_URL: 3;
7
+ }>;
8
+
9
+ export interface StickerArea {
10
+ /** Distance from the left edge, as a fraction of the media width. */
11
+ x: number;
12
+ /** Distance from the top edge, as a fraction of the media height. */
13
+ y: number;
14
+ width: number;
15
+ height: number;
16
+ }
17
+
18
+ export declare const STICKER_DEFAULT_AREA: Readonly<StickerArea>;
19
+
20
+ export declare function stickerArea(area?: StickerArea): proto.IPoint[];
21
+
22
+ export declare function locationSticker(options: {
23
+ latitude: number;
24
+ longitude: number;
25
+ name?: string;
26
+ area?: StickerArea | proto.IPoint[];
27
+ skipConfirmation?: boolean;
28
+ }): proto.IInteractiveAnnotation;
29
+
30
+ export declare function channelSticker(options: {
31
+ jid: string;
32
+ name?: string;
33
+ serverMessageId?: number;
34
+ accessibilityText?: string;
35
+ area?: StickerArea | proto.IPoint[];
36
+ }): proto.IInteractiveAnnotation;
37
+
38
+ export declare function linkSticker(options: {
39
+ url: string;
40
+ title?: string;
41
+ area?: StickerArea | proto.IPoint[];
42
+ linkType?: number;
43
+ }): proto.IInteractiveAnnotation;
44
+
45
+ export declare function musicSticker(options: {
46
+ songId?: string;
47
+ title?: string;
48
+ author?: string;
49
+ mediaId?: string;
50
+ artworkDirectPath?: string;
51
+ artworkSha256?: Buffer | string;
52
+ artworkEncSha256?: Buffer | string;
53
+ artworkMediaKey?: Buffer | string;
54
+ artistAttribution?: string;
55
+ countryBlocklist?: Buffer | string;
56
+ isExplicit?: boolean;
57
+ startTimeMs?: number;
58
+ derivedStartTimeMs?: number;
59
+ durationMs?: number;
60
+ area?: StickerArea | proto.IPoint[];
61
+ }): proto.IInteractiveAnnotation;
62
+
63
+ export declare function messageSticker(options: {
64
+ stanzaId?: string;
65
+ message: proto.IMessage;
66
+ area?: StickerArea | proto.IPoint[];
67
+ }): proto.IInteractiveAnnotation;
68
+
69
+ export declare function normalizeStickers(
70
+ stickers: proto.IInteractiveAnnotation | proto.IInteractiveAnnotation[]
71
+ ): proto.InteractiveAnnotation[];
72
+
73
+ export interface ReadSticker {
74
+ kind: 'location' | 'channel' | 'link' | 'music' | 'message' | 'unknown';
75
+ area?: StickerArea;
76
+ location?: proto.ILocation;
77
+ channel?: proto.ContextInfo.IForwardedNewsletterMessageInfo;
78
+ link?: { url?: string; title?: string; linkType?: number };
79
+ music?: { songId?: string; title?: string; author?: string };
80
+ message?: proto.IEmbeddedMessage;
81
+ annotation: proto.IInteractiveAnnotation;
82
+ }
83
+
84
+ export declare function readStickers(message: any): ReadSticker[];
85
+
86
+ export declare const MusicMessageStyle: Readonly<{ UNKNOWN: 0; VINYL: 1 }>;
87
+
88
+ export interface MusicMessageOptions {
89
+ songId?: string;
90
+ mediaId?: string;
91
+ title?: string;
92
+ author?: string;
93
+ artistAttribution?: string;
94
+ artworkDirectPath?: string;
95
+ artworkSha256?: Buffer | string;
96
+ artworkEncSha256?: Buffer | string;
97
+ artworkMediaKey?: Buffer | string;
98
+ countryBlocklist?: Buffer | string;
99
+ isExplicit?: boolean;
100
+ startTimeMs?: number;
101
+ derivedStartTimeMs?: number;
102
+ durationMs?: number;
103
+ songUri?: string;
104
+ artworkUri?: string;
105
+ style?: number;
106
+ contextInfo?: proto.IContextInfo;
107
+ }
108
+
109
+ export declare function buildMusicMessage(options?: MusicMessageOptions): proto.Message.IMusicMessage;
110
+
111
+ export declare function readMusicMessage(msg: any): {
112
+ songId?: string;
113
+ mediaId?: string;
114
+ title?: string;
115
+ author?: string;
116
+ artistAttribution?: string;
117
+ isExplicit?: boolean;
118
+ startTimeMs?: number | string;
119
+ durationMs?: number | string;
120
+ songUri?: string;
121
+ artworkUri?: string;
122
+ style?: number;
123
+ embeddedMusic: proto.IEmbeddedMusic;
124
+ } | null;
125
+
126
+ export declare const MUSIC_ALLOWED_HOSTS: readonly string[];
127
+ export declare function isMusicHostAllowed(value?: unknown): boolean;
@@ -0,0 +1,260 @@
1
+ import { proto } from '../../WAProto/index.js'
2
+
3
+ export const StatusLinkType = Object.freeze({
4
+ RASTERIZED_LINK_PREVIEW: 1,
5
+ RASTERIZED_LINK_TRUNCATED: 2,
6
+ RASTERIZED_LINK_FULL_URL: 3
7
+ })
8
+
9
+ export const STICKER_DEFAULT_AREA = Object.freeze({ x: 0.25, y: 0.4, width: 0.5, height: 0.2 })
10
+
11
+ const trimUndefined = (object) => {
12
+ for (const key of Object.keys(object)) {
13
+ if (object[key] === undefined) {
14
+ delete object[key]
15
+ }
16
+ }
17
+ return object
18
+ }
19
+
20
+ const fraction = (value, name) => {
21
+ const number = Number(value)
22
+ if (!Number.isFinite(number) || number < 0 || number > 1) {
23
+ throw new TypeError(`${name} must be between 0 and 1, the client reads these as a fraction of the media`)
24
+ }
25
+ return number
26
+ }
27
+
28
+ export const stickerArea = ({ x, y, width, height } = STICKER_DEFAULT_AREA) => {
29
+ const left = fraction(x, 'x')
30
+ const top = fraction(y, 'y')
31
+ const right = fraction(left + Number(width), 'x + width')
32
+ const bottom = fraction(top + Number(height), 'y + height')
33
+ if (right <= left || bottom <= top) {
34
+ throw new TypeError('a sticker area needs a positive width and height')
35
+ }
36
+ return [
37
+ { x: left, y: top },
38
+ { x: right, y: top },
39
+ { x: right, y: bottom },
40
+ { x: left, y: bottom }
41
+ ]
42
+ }
43
+
44
+ const annotation = (area, fields) => ({
45
+ polygonVertices: Array.isArray(area) ? area : stickerArea(area),
46
+ ...fields
47
+ })
48
+
49
+ export const locationSticker = ({ latitude, longitude, name, area, skipConfirmation } = {}) => {
50
+ const degreesLatitude = Number(latitude)
51
+ const degreesLongitude = Number(longitude)
52
+ if (!Number.isFinite(degreesLatitude) || !Number.isFinite(degreesLongitude)) {
53
+ throw new TypeError('locationSticker needs a numeric latitude and longitude')
54
+ }
55
+ return annotation(area, {
56
+ location: { degreesLatitude, degreesLongitude, ...(name ? { name } : {}) },
57
+ ...(skipConfirmation === undefined ? {} : { shouldSkipConfirmation: !!skipConfirmation })
58
+ })
59
+ }
60
+
61
+ export const channelSticker = ({ jid, name, serverMessageId = 0, accessibilityText, area } = {}) => {
62
+ if (typeof jid !== 'string' || !jid.endsWith('@newsletter')) {
63
+ throw new TypeError('channelSticker needs the channel jid, the one ending in @newsletter')
64
+ }
65
+ return annotation(area, {
66
+ newsletter: {
67
+ newsletterJid: jid,
68
+ serverMessageId: Number(serverMessageId),
69
+ ...(name ? { newsletterName: name } : {}),
70
+ ...(accessibilityText ? { accessibilityText } : {})
71
+ }
72
+ })
73
+ }
74
+
75
+ export const linkSticker = ({ url, title, area, linkType = StatusLinkType.RASTERIZED_LINK_PREVIEW } = {}) => {
76
+ if (typeof url !== 'string' || !url) {
77
+ throw new TypeError('linkSticker needs a url')
78
+ }
79
+ return annotation(area, {
80
+ tapAction: { tapUrl: url, ...(title ? { title } : {}) },
81
+ statusLinkType: linkType
82
+ })
83
+ }
84
+
85
+ export const musicSticker = ({
86
+ songId,
87
+ title,
88
+ author,
89
+ mediaId,
90
+ artworkDirectPath,
91
+ artworkSha256,
92
+ artworkEncSha256,
93
+ artworkMediaKey,
94
+ artistAttribution,
95
+ countryBlocklist,
96
+ isExplicit,
97
+ startTimeMs = 0,
98
+ derivedStartTimeMs,
99
+ durationMs,
100
+ area
101
+ } = {}) => {
102
+ if (!songId && !mediaId) {
103
+ throw new TypeError('musicSticker needs at least a songId or a musicContentMediaId')
104
+ }
105
+ const bytes = value => (value === undefined ? undefined : Buffer.isBuffer(value) ? value : Buffer.from(value, 'base64'))
106
+ const embeddedMusic = {
107
+ ...(songId ? { songId: String(songId) } : {}),
108
+ ...(mediaId ? { musicContentMediaId: String(mediaId) } : {}),
109
+ ...(title ? { title } : {}),
110
+ ...(author ? { author } : {}),
111
+ ...(artistAttribution ? { artistAttribution } : {}),
112
+ ...(artworkDirectPath ? { artworkDirectPath } : {}),
113
+ ...(isExplicit === undefined ? {} : { isExplicit: !!isExplicit }),
114
+ musicSongStartTimeInMs: Number(startTimeMs),
115
+ ...(derivedStartTimeMs === undefined ? {} : { derivedContentStartTimeInMs: Number(derivedStartTimeMs) }),
116
+ ...(durationMs === undefined ? {} : { overlapDurationInMs: Number(durationMs) })
117
+ }
118
+ for (const [key, value] of [
119
+ ['artworkSha256', artworkSha256],
120
+ ['artworkEncSha256', artworkEncSha256],
121
+ ['artworkMediaKey', artworkMediaKey],
122
+ ['countryBlocklist', countryBlocklist]
123
+ ]) {
124
+ const buffer = bytes(value)
125
+ if (buffer) {
126
+ embeddedMusic[key] = buffer
127
+ }
128
+ }
129
+ return annotation(area, { embeddedContent: { embeddedMusic } })
130
+ }
131
+
132
+ export const messageSticker = ({ stanzaId, message, area } = {}) => {
133
+ if (!message) {
134
+ throw new TypeError('messageSticker needs the message it embeds')
135
+ }
136
+ return annotation(area, {
137
+ embeddedContent: { embeddedMessage: { ...(stanzaId ? { stanzaId } : {}), message } }
138
+ })
139
+ }
140
+
141
+ const ACTIONS = ['location', 'newsletter', 'embeddedAction', 'tapAction']
142
+
143
+ export const normalizeStickers = (stickers) => {
144
+ const list = Array.isArray(stickers) ? stickers : [stickers]
145
+ return list.filter(Boolean).map((sticker, index) => {
146
+ const vertices = sticker.polygonVertices
147
+ if (!Array.isArray(vertices) || vertices.length !== 4) {
148
+ throw new TypeError(`sticker ${index} needs four polygonVertices, the client reads corner 0 and corner 2`)
149
+ }
150
+ const actions = ACTIONS.filter(name => sticker[name] !== undefined && sticker[name] !== null)
151
+ if (actions.length > 1) {
152
+ throw new TypeError(`sticker ${index} sets ${actions.join(' and ')}, but the client reads only one action per sticker`)
153
+ }
154
+ return proto.InteractiveAnnotation.fromObject(sticker)
155
+ })
156
+ }
157
+
158
+ export const readStickers = (message) => {
159
+ const media = message?.imageMessage ?? message?.videoMessage ?? message
160
+ const annotations = media?.interactiveAnnotations
161
+ if (!Array.isArray(annotations) || !annotations.length) {
162
+ return []
163
+ }
164
+ return annotations.map(item => {
165
+ const [topLeft, , bottomRight] = item.polygonVertices ?? []
166
+ const music = item.embeddedContent?.embeddedMusic
167
+ return {
168
+ kind: item.location
169
+ ? 'location'
170
+ : item.newsletter
171
+ ? 'channel'
172
+ : item.tapAction
173
+ ? 'link'
174
+ : music
175
+ ? 'music'
176
+ : item.embeddedContent?.embeddedMessage
177
+ ? 'message'
178
+ : 'unknown',
179
+ area: topLeft && bottomRight
180
+ ? { x: topLeft.x, y: topLeft.y, width: bottomRight.x - topLeft.x, height: bottomRight.y - topLeft.y }
181
+ : undefined,
182
+ location: item.location ?? undefined,
183
+ channel: item.newsletter ?? undefined,
184
+ link: item.tapAction ? { url: item.tapAction.tapUrl, title: item.tapAction.title, linkType: item.statusLinkType } : undefined,
185
+ music: music ? { songId: music.songId, title: music.title, author: music.author } : undefined,
186
+ message: item.embeddedContent?.embeddedMessage ?? undefined,
187
+ annotation: item
188
+ }
189
+ })
190
+ }
191
+
192
+ export const MusicMessageStyle = Object.freeze({ UNKNOWN: 0, VINYL: 1 })
193
+
194
+ export const MUSIC_ALLOWED_HOSTS = Object.freeze([
195
+ '.whatsapp.net',
196
+ '.whatsapp.com',
197
+ '.fbcdn.net',
198
+ '.facebook.com',
199
+ '.instagram.com',
200
+ '.cdninstagram.com'
201
+ ])
202
+
203
+ export const isMusicHostAllowed = (value) => {
204
+ let host
205
+ try {
206
+ host = new URL(String(value)).hostname.toLowerCase()
207
+ }
208
+ catch {
209
+ return false
210
+ }
211
+ return MUSIC_ALLOWED_HOSTS.some(suffix => host.endsWith(suffix) || host === suffix.slice(1))
212
+ }
213
+
214
+ const assertMusicHost = (value, label) => {
215
+ if (value === undefined) {
216
+ return undefined
217
+ }
218
+ if (!isMusicHostAllowed(value)) {
219
+ throw new TypeError(
220
+ `${label} must be hosted on ${MUSIC_ALLOWED_HOSTS.join(', ')} — the client refuses any other host `
221
+ + 'and draws nothing. Both uris come from Meta music catalog entries, so carry them over from a '
222
+ + 'message you received rather than pointing at your own file'
223
+ )
224
+ }
225
+ return value
226
+ }
227
+
228
+ const embeddedMusicOf = (options) => musicSticker(options).embeddedContent.embeddedMusic
229
+
230
+ export const buildMusicMessage = ({ songUri, artworkUri, style = MusicMessageStyle.VINYL, contextInfo, ...music } = {}) =>
231
+ trimUndefined({
232
+ embeddedMusic: embeddedMusicOf(music),
233
+ songUri: assertMusicHost(songUri, 'songUri'),
234
+ artworkUri: assertMusicHost(artworkUri, 'artworkUri'),
235
+ style,
236
+ contextInfo
237
+ })
238
+
239
+ export const readMusicMessage = (msg) => {
240
+ const message = msg?.message ?? msg
241
+ const music = message?.musicMessage
242
+ if (!music) {
243
+ return null
244
+ }
245
+ const embedded = music.embeddedMusic ?? {}
246
+ return {
247
+ songId: embedded.songId,
248
+ mediaId: embedded.musicContentMediaId,
249
+ title: embedded.title,
250
+ author: embedded.author,
251
+ artistAttribution: embedded.artistAttribution,
252
+ isExplicit: embedded.isExplicit,
253
+ startTimeMs: embedded.musicSongStartTimeInMs,
254
+ durationMs: embedded.overlapDurationInMs,
255
+ songUri: music.songUri,
256
+ artworkUri: music.artworkUri,
257
+ style: music.style,
258
+ embeddedMusic: embedded
259
+ }
260
+ }
@@ -0,0 +1,2 @@
1
+ export function processContactAction(action: any, id: any, logger: any): any[];
2
+ export function emitSyncActionResults(ev: any, results: any): void;
@@ -0,0 +1,44 @@
1
+ import { isLidUser, isPnUser } from '../WABinary/index.js';
2
+
3
+ export const processContactAction = (action, id, logger) => {
4
+ const results = [];
5
+ if (!id) {
6
+ logger?.warn({ hasFullName: !!action.fullName, hasLidJid: !!action.lidJid, hasPnJid: !!action.pnJid }, 'contactAction sync: missing id in index');
7
+ return results;
8
+ }
9
+ const lidJid = action.lidJid;
10
+ const idIsPn = isPnUser(id);
11
+
12
+ const phoneNumber = idIsPn ? id : action.pnJid || undefined;
13
+
14
+ results.push({
15
+ event: 'contacts.upsert',
16
+ data: [
17
+ {
18
+ id,
19
+ name: action.fullName || action.firstName || action.username || undefined,
20
+ username: action.username || undefined,
21
+ lid: lidJid || undefined,
22
+ phoneNumber
23
+ }
24
+ ]
25
+ });
26
+
27
+ if (lidJid && isLidUser(lidJid) && idIsPn) {
28
+ results.push({
29
+ event: 'lid-mapping.update',
30
+ data: { lid: lidJid, pn: id }
31
+ });
32
+ }
33
+ return results;
34
+ };
35
+ export const emitSyncActionResults = (ev, results) => {
36
+ for (const result of results) {
37
+ if (result.event === 'contacts.upsert') {
38
+ ev.emit('contacts.upsert', result.data);
39
+ }
40
+ else {
41
+ ev.emit('lid-mapping.update', result.data);
42
+ }
43
+ }
44
+ };