@sixcore/baileys 1.0.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 (278) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +606 -0
  3. package/WAProto/GenerateStatics.sh +4 -0
  4. package/WAProto/WAProto.proto +4357 -0
  5. package/WAProto/index.d.ts +50383 -0
  6. package/WAProto/index.js +155693 -0
  7. package/WASignalGroup/GroupProtocol.js +1697 -0
  8. package/WASignalGroup/ciphertext_message.js +16 -0
  9. package/WASignalGroup/generate-proto.sh +1 -0
  10. package/WASignalGroup/group.proto +42 -0
  11. package/WASignalGroup/group_cipher.js +120 -0
  12. package/WASignalGroup/group_session_builder.js +46 -0
  13. package/WASignalGroup/index.js +5 -0
  14. package/WASignalGroup/keyhelper.js +21 -0
  15. package/WASignalGroup/protobufs.js +3 -0
  16. package/WASignalGroup/queue_job.js +69 -0
  17. package/WASignalGroup/sender_chain_key.js +50 -0
  18. package/WASignalGroup/sender_key_distribution_message.js +78 -0
  19. package/WASignalGroup/sender_key_message.js +92 -0
  20. package/WASignalGroup/sender_key_name.js +70 -0
  21. package/WASignalGroup/sender_key_record.js +56 -0
  22. package/WASignalGroup/sender_key_state.js +129 -0
  23. package/WASignalGroup/sender_message_key.js +39 -0
  24. package/lib/Defaults/baileys-version.json +3 -0
  25. package/lib/Defaults/index.d.ts +53 -0
  26. package/lib/Defaults/index.js +108 -0
  27. package/lib/Signal/libsignal.d.ts +3 -0
  28. package/lib/Signal/libsignal.js +152 -0
  29. package/lib/Socket/Client/abstract-socket-client.d.ts +17 -0
  30. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  31. package/lib/Socket/Client/index.d.ts +3 -0
  32. package/lib/Socket/Client/index.js +19 -0
  33. package/lib/Socket/Client/mobile-socket-client.d.ts +13 -0
  34. package/lib/Socket/Client/mobile-socket-client.js +65 -0
  35. package/lib/Socket/Client/web-socket-client.d.ts +12 -0
  36. package/lib/Socket/Client/web-socket-client.js +62 -0
  37. package/lib/Socket/business.d.ts +170 -0
  38. package/lib/Socket/business.js +260 -0
  39. package/lib/Socket/chats.d.ts +81 -0
  40. package/lib/Socket/chats.js +950 -0
  41. package/lib/Socket/groups.d.ts +115 -0
  42. package/lib/Socket/groups.js +315 -0
  43. package/lib/Socket/index.d.ts +172 -0
  44. package/lib/Socket/index.js +10 -0
  45. package/lib/Socket/messages-recv.d.ts +158 -0
  46. package/lib/Socket/messages-recv.js +972 -0
  47. package/lib/Socket/messages-send.d.ts +155 -0
  48. package/lib/Socket/messages-send.js +1087 -0
  49. package/lib/Socket/newsletter.d.ts +132 -0
  50. package/lib/Socket/newsletter.js +236 -0
  51. package/lib/Socket/registration.d.ts +264 -0
  52. package/lib/Socket/registration.js +166 -0
  53. package/lib/Socket/socket.d.ts +44 -0
  54. package/lib/Socket/socket.js +643 -0
  55. package/lib/Socket/usync.d.ts +37 -0
  56. package/lib/Socket/usync.js +70 -0
  57. package/lib/Store/index.d.ts +3 -0
  58. package/lib/Store/index.js +10 -0
  59. package/lib/Store/make-cache-manager-store.d.ts +14 -0
  60. package/lib/Store/make-cache-manager-store.js +83 -0
  61. package/lib/Store/make-in-memory-store.d.ts +118 -0
  62. package/lib/Store/make-in-memory-store.js +431 -0
  63. package/lib/Store/make-ordered-dictionary.d.ts +13 -0
  64. package/lib/Store/make-ordered-dictionary.js +81 -0
  65. package/lib/Store/object-repository.d.ts +10 -0
  66. package/lib/Store/object-repository.js +27 -0
  67. package/lib/Types/Auth.d.ts +109 -0
  68. package/lib/Types/Auth.js +2 -0
  69. package/lib/Types/Call.d.ts +13 -0
  70. package/lib/Types/Call.js +2 -0
  71. package/lib/Types/Chat.d.ts +107 -0
  72. package/lib/Types/Chat.js +4 -0
  73. package/lib/Types/Contact.d.ts +19 -0
  74. package/lib/Types/Contact.js +2 -0
  75. package/lib/Types/Events.d.ts +172 -0
  76. package/lib/Types/Events.js +2 -0
  77. package/lib/Types/GroupMetadata.d.ts +56 -0
  78. package/lib/Types/GroupMetadata.js +2 -0
  79. package/lib/Types/Label.d.ts +46 -0
  80. package/lib/Types/Label.js +27 -0
  81. package/lib/Types/LabelAssociation.d.ts +29 -0
  82. package/lib/Types/LabelAssociation.js +9 -0
  83. package/lib/Types/Message.d.ts +433 -0
  84. package/lib/Types/Message.js +9 -0
  85. package/lib/Types/Newsletter.d.ts +92 -0
  86. package/lib/Types/Newsletter.js +32 -0
  87. package/lib/Types/Product.d.ts +78 -0
  88. package/lib/Types/Product.js +2 -0
  89. package/lib/Types/Signal.d.ts +57 -0
  90. package/lib/Types/Signal.js +2 -0
  91. package/lib/Types/Socket.d.ts +116 -0
  92. package/lib/Types/Socket.js +2 -0
  93. package/lib/Types/State.d.ts +27 -0
  94. package/lib/Types/State.js +2 -0
  95. package/lib/Types/USync.d.ts +25 -0
  96. package/lib/Types/USync.js +2 -0
  97. package/lib/Types/index.d.ts +66 -0
  98. package/lib/Types/index.js +42 -0
  99. package/lib/Utils/auth-utils.d.ts +18 -0
  100. package/lib/Utils/auth-utils.js +227 -0
  101. package/lib/Utils/baileys-event-stream.d.ts +16 -0
  102. package/lib/Utils/baileys-event-stream.js +63 -0
  103. package/lib/Utils/business.d.ts +22 -0
  104. package/lib/Utils/business.js +234 -0
  105. package/lib/Utils/chat-utils.d.ts +70 -0
  106. package/lib/Utils/chat-utils.js +745 -0
  107. package/lib/Utils/crypto.d.ts +40 -0
  108. package/lib/Utils/crypto.js +199 -0
  109. package/lib/Utils/decode-wa-message.d.ts +36 -0
  110. package/lib/Utils/decode-wa-message.js +234 -0
  111. package/lib/Utils/event-buffer.d.ts +35 -0
  112. package/lib/Utils/event-buffer.js +517 -0
  113. package/lib/Utils/generics.d.ts +88 -0
  114. package/lib/Utils/generics.js +402 -0
  115. package/lib/Utils/history.d.ts +19 -0
  116. package/lib/Utils/history.js +94 -0
  117. package/lib/Utils/index.d.ts +17 -0
  118. package/lib/Utils/index.js +33 -0
  119. package/lib/Utils/link-preview.d.ts +21 -0
  120. package/lib/Utils/link-preview.js +93 -0
  121. package/lib/Utils/logger.d.ts +2 -0
  122. package/lib/Utils/logger.js +7 -0
  123. package/lib/Utils/lt-hash.d.ts +12 -0
  124. package/lib/Utils/lt-hash.js +51 -0
  125. package/lib/Utils/make-mutex.d.ts +7 -0
  126. package/lib/Utils/make-mutex.js +43 -0
  127. package/lib/Utils/messages-media.d.ts +113 -0
  128. package/lib/Utils/messages-media.js +643 -0
  129. package/lib/Utils/messages.d.ts +77 -0
  130. package/lib/Utils/messages.js +1221 -0
  131. package/lib/Utils/noise-handler.d.ts +20 -0
  132. package/lib/Utils/noise-handler.js +160 -0
  133. package/lib/Utils/process-message.d.ts +41 -0
  134. package/lib/Utils/process-message.js +373 -0
  135. package/lib/Utils/signal.d.ts +33 -0
  136. package/lib/Utils/signal.js +159 -0
  137. package/lib/Utils/use-multi-file-auth-state.d.ts +12 -0
  138. package/lib/Utils/use-multi-file-auth-state.js +295 -0
  139. package/lib/Utils/use-single-file-auth-state.d.ts +12 -0
  140. package/lib/Utils/use-single-file-auth-state.js +75 -0
  141. package/lib/Utils/validate-connection.d.ts +11 -0
  142. package/lib/Utils/validate-connection.js +205 -0
  143. package/lib/WABinary/constants.d.ts +27 -0
  144. package/lib/WABinary/constants.js +40 -0
  145. package/lib/WABinary/decode.d.ts +6 -0
  146. package/lib/WABinary/decode.js +264 -0
  147. package/lib/WABinary/encode.d.ts +2 -0
  148. package/lib/WABinary/encode.js +252 -0
  149. package/lib/WABinary/generic-utils.d.ts +14 -0
  150. package/lib/WABinary/generic-utils.js +110 -0
  151. package/lib/WABinary/index.d.ts +5 -0
  152. package/lib/WABinary/index.js +21 -0
  153. package/lib/WABinary/jid-utils.d.ts +31 -0
  154. package/lib/WABinary/jid-utils.js +62 -0
  155. package/lib/WABinary/types.d.ts +18 -0
  156. package/lib/WABinary/types.js +2 -0
  157. package/lib/WAM/BinaryInfo.d.ts +8 -0
  158. package/lib/WAM/BinaryInfo.js +13 -0
  159. package/lib/WAM/constants.d.ts +38 -0
  160. package/lib/WAM/constants.js +15350 -0
  161. package/lib/WAM/encode.d.ts +2 -0
  162. package/lib/WAM/encode.js +155 -0
  163. package/lib/WAM/index.d.ts +3 -0
  164. package/lib/WAM/index.js +19 -0
  165. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -0
  166. package/lib/WAUSync/Protocols/USyncContactProtocol.js +32 -0
  167. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
  168. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  169. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
  170. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  171. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
  172. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
  173. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +25 -0
  174. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -0
  175. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +8 -0
  176. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  177. package/lib/WAUSync/Protocols/index.d.ts +4 -0
  178. package/lib/WAUSync/Protocols/index.js +20 -0
  179. package/lib/WAUSync/USyncQuery.d.ts +28 -0
  180. package/lib/WAUSync/USyncQuery.js +89 -0
  181. package/lib/WAUSync/USyncUser.d.ts +10 -0
  182. package/lib/WAUSync/USyncUser.js +26 -0
  183. package/lib/WAUSync/index.d.ts +3 -0
  184. package/lib/WAUSync/index.js +19 -0
  185. package/lib/index.js +31 -0
  186. package/package.json +51 -0
  187. package/src/Defaults/baileys-version.json +3 -0
  188. package/src/Defaults/index.ts +133 -0
  189. package/src/Signal/Group/ciphertext-message.ts +9 -0
  190. package/src/Signal/Group/group-session-builder.ts +56 -0
  191. package/src/Signal/Group/group_cipher.ts +117 -0
  192. package/src/Signal/Group/index.ts +11 -0
  193. package/src/Signal/Group/keyhelper.ts +28 -0
  194. package/src/Signal/Group/sender-chain-key.ts +34 -0
  195. package/src/Signal/Group/sender-key-distribution-message.ts +95 -0
  196. package/src/Signal/Group/sender-key-message.ts +96 -0
  197. package/src/Signal/Group/sender-key-name.ts +66 -0
  198. package/src/Signal/Group/sender-key-record.ts +69 -0
  199. package/src/Signal/Group/sender-key-state.ts +134 -0
  200. package/src/Signal/Group/sender-message-key.ts +36 -0
  201. package/src/Signal/libsignal.ts +447 -0
  202. package/src/Signal/lid-mapping.ts +209 -0
  203. package/src/Socket/Client/index.ts +2 -0
  204. package/src/Socket/Client/types.ts +22 -0
  205. package/src/Socket/Client/websocket.ts +56 -0
  206. package/src/Socket/business.ts +421 -0
  207. package/src/Socket/chats.ts +1223 -0
  208. package/src/Socket/communities.ts +477 -0
  209. package/src/Socket/groups.ts +361 -0
  210. package/src/Socket/index.ts +22 -0
  211. package/src/Socket/messages-recv.ts +1563 -0
  212. package/src/Socket/messages-send.ts +1210 -0
  213. package/src/Socket/mex.ts +58 -0
  214. package/src/Socket/newsletter.ts +229 -0
  215. package/src/Socket/socket.ts +1072 -0
  216. package/src/Types/Auth.ts +115 -0
  217. package/src/Types/Bussines.ts +20 -0
  218. package/src/Types/Call.ts +14 -0
  219. package/src/Types/Chat.ts +138 -0
  220. package/src/Types/Contact.ts +24 -0
  221. package/src/Types/Events.ts +132 -0
  222. package/src/Types/GroupMetadata.ts +70 -0
  223. package/src/Types/Label.ts +48 -0
  224. package/src/Types/LabelAssociation.ts +35 -0
  225. package/src/Types/Message.ts +424 -0
  226. package/src/Types/Newsletter.ts +98 -0
  227. package/src/Types/Product.ts +85 -0
  228. package/src/Types/Signal.ts +76 -0
  229. package/src/Types/Socket.ts +150 -0
  230. package/src/Types/State.ts +43 -0
  231. package/src/Types/USync.ts +27 -0
  232. package/src/Types/globals.d.ts +8 -0
  233. package/src/Types/index.ts +67 -0
  234. package/src/Utils/auth-utils.ts +331 -0
  235. package/src/Utils/browser-utils.ts +31 -0
  236. package/src/Utils/business.ts +286 -0
  237. package/src/Utils/chat-utils.ts +933 -0
  238. package/src/Utils/crypto.ts +184 -0
  239. package/src/Utils/decode-wa-message.ts +355 -0
  240. package/src/Utils/event-buffer.ts +662 -0
  241. package/src/Utils/generics.ts +470 -0
  242. package/src/Utils/history.ts +114 -0
  243. package/src/Utils/index.ts +18 -0
  244. package/src/Utils/link-preview.ts +111 -0
  245. package/src/Utils/logger.ts +13 -0
  246. package/src/Utils/lt-hash.ts +65 -0
  247. package/src/Utils/make-mutex.ts +45 -0
  248. package/src/Utils/message-retry-manager.ts +229 -0
  249. package/src/Utils/messages-media.ts +820 -0
  250. package/src/Utils/messages.ts +1137 -0
  251. package/src/Utils/noise-handler.ts +192 -0
  252. package/src/Utils/pre-key-manager.ts +126 -0
  253. package/src/Utils/process-message.ts +622 -0
  254. package/src/Utils/signal.ts +214 -0
  255. package/src/Utils/use-multi-file-auth-state.ts +136 -0
  256. package/src/Utils/validate-connection.ts +253 -0
  257. package/src/WABinary/constants.ts +1305 -0
  258. package/src/WABinary/decode.ts +281 -0
  259. package/src/WABinary/encode.ts +253 -0
  260. package/src/WABinary/generic-utils.ts +127 -0
  261. package/src/WABinary/index.ts +5 -0
  262. package/src/WABinary/jid-utils.ts +128 -0
  263. package/src/WABinary/types.ts +17 -0
  264. package/src/WAM/BinaryInfo.ts +12 -0
  265. package/src/WAM/constants.ts +22889 -0
  266. package/src/WAM/encode.ts +169 -0
  267. package/src/WAM/index.ts +3 -0
  268. package/src/WAUSync/Protocols/USyncContactProtocol.ts +32 -0
  269. package/src/WAUSync/Protocols/USyncDeviceProtocol.ts +78 -0
  270. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.ts +35 -0
  271. package/src/WAUSync/Protocols/USyncStatusProtocol.ts +44 -0
  272. package/src/WAUSync/Protocols/UsyncBotProfileProtocol.ts +76 -0
  273. package/src/WAUSync/Protocols/UsyncLIDProtocol.ts +33 -0
  274. package/src/WAUSync/Protocols/index.ts +4 -0
  275. package/src/WAUSync/USyncQuery.ts +133 -0
  276. package/src/WAUSync/USyncUser.ts +32 -0
  277. package/src/WAUSync/index.ts +3 -0
  278. package/src/index.ts +13 -0
@@ -0,0 +1,447 @@
1
+ /* @ts-ignore */
2
+ import * as libsignal from 'libsignal'
3
+ import { LRUCache } from 'lru-cache'
4
+ import type { LIDMapping, SignalAuthState, SignalKeyStoreWithTransaction } from '../Types'
5
+ import type { SignalRepositoryWithLIDStore } from '../Types/Signal'
6
+ import { generateSignalPubKey } from '../Utils'
7
+ import type { ILogger } from '../Utils/logger'
8
+ import {
9
+ isHostedLidUser,
10
+ isHostedPnUser,
11
+ isLidUser,
12
+ isPnUser,
13
+ jidDecode,
14
+ transferDevice,
15
+ WAJIDDomains
16
+ } from '../WABinary'
17
+ import type { SenderKeyStore } from './Group/group_cipher'
18
+ import { SenderKeyName } from './Group/sender-key-name'
19
+ import { SenderKeyRecord } from './Group/sender-key-record'
20
+ import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage } from './Group'
21
+ import { LIDMappingStore } from './lid-mapping'
22
+
23
+ export function makeLibSignalRepository(
24
+ auth: SignalAuthState,
25
+ logger: ILogger,
26
+ pnToLIDFunc?: (jids: string[]) => Promise<LIDMapping[] | undefined>
27
+ ): SignalRepositoryWithLIDStore {
28
+ const lidMapping = new LIDMappingStore(auth.keys as SignalKeyStoreWithTransaction, logger, pnToLIDFunc)
29
+ const storage = signalStorage(auth, lidMapping)
30
+
31
+ const parsedKeys = auth.keys as SignalKeyStoreWithTransaction
32
+ const migratedSessionCache = new LRUCache<string, true>({
33
+ ttl: 3 * 24 * 60 * 60 * 1000, // 7 days
34
+ ttlAutopurge: true,
35
+ updateAgeOnGet: true
36
+ })
37
+
38
+ const repository: SignalRepositoryWithLIDStore = {
39
+ decryptGroupMessage({ group, authorJid, msg }) {
40
+ const senderName = jidToSignalSenderKeyName(group, authorJid)
41
+ const cipher = new GroupCipher(storage, senderName)
42
+
43
+ // Use transaction to ensure atomicity
44
+ return parsedKeys.transaction(async () => {
45
+ return cipher.decrypt(msg)
46
+ }, group)
47
+ },
48
+ async processSenderKeyDistributionMessage({ item, authorJid }) {
49
+ const builder = new GroupSessionBuilder(storage)
50
+ if (!item.groupId) {
51
+ throw new Error('Group ID is required for sender key distribution message')
52
+ }
53
+
54
+ const senderName = jidToSignalSenderKeyName(item.groupId, authorJid)
55
+
56
+ const senderMsg = new SenderKeyDistributionMessage(
57
+ null,
58
+ null,
59
+ null,
60
+ null,
61
+ item.axolotlSenderKeyDistributionMessage
62
+ )
63
+ const senderNameStr = senderName.toString()
64
+ const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr])
65
+ if (!senderKey) {
66
+ await storage.storeSenderKey(senderName, new SenderKeyRecord())
67
+ }
68
+
69
+ return parsedKeys.transaction(async () => {
70
+ const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr])
71
+ if (!senderKey) {
72
+ await storage.storeSenderKey(senderName, new SenderKeyRecord())
73
+ }
74
+
75
+ await builder.process(senderName, senderMsg)
76
+ }, item.groupId)
77
+ },
78
+ async decryptMessage({ jid, type, ciphertext }) {
79
+ const addr = jidToSignalProtocolAddress(jid)
80
+ const session = new libsignal.SessionCipher(storage, addr)
81
+
82
+ async function doDecrypt() {
83
+ let result: Buffer
84
+ switch (type) {
85
+ case 'pkmsg':
86
+ result = await session.decryptPreKeyWhisperMessage(ciphertext)
87
+ break
88
+ case 'msg':
89
+ result = await session.decryptWhisperMessage(ciphertext)
90
+ break
91
+ }
92
+
93
+ return result
94
+ }
95
+
96
+ // If it's not a sync message, we need to ensure atomicity
97
+ // For regular messages, we use a transaction to ensure atomicity
98
+ return parsedKeys.transaction(async () => {
99
+ return await doDecrypt()
100
+ }, jid)
101
+ },
102
+
103
+ async encryptMessage({ jid, data }) {
104
+ const addr = jidToSignalProtocolAddress(jid)
105
+ const cipher = new libsignal.SessionCipher(storage, addr)
106
+
107
+ // Use transaction to ensure atomicity
108
+ return parsedKeys.transaction(async () => {
109
+ const { type: sigType, body } = await cipher.encrypt(data)
110
+ const type = sigType === 3 ? 'pkmsg' : 'msg'
111
+ return { type, ciphertext: Buffer.from(body, 'binary') }
112
+ }, jid)
113
+ },
114
+
115
+ async encryptGroupMessage({ group, meId, data }) {
116
+ const senderName = jidToSignalSenderKeyName(group, meId)
117
+ const builder = new GroupSessionBuilder(storage)
118
+
119
+ const senderNameStr = senderName.toString()
120
+
121
+ return parsedKeys.transaction(async () => {
122
+ const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr])
123
+ if (!senderKey) {
124
+ await storage.storeSenderKey(senderName, new SenderKeyRecord())
125
+ }
126
+
127
+ const senderKeyDistributionMessage = await builder.create(senderName)
128
+ const session = new GroupCipher(storage, senderName)
129
+ const ciphertext = await session.encrypt(data)
130
+
131
+ return {
132
+ ciphertext,
133
+ senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
134
+ }
135
+ }, group)
136
+ },
137
+
138
+ async injectE2ESession({ jid, session }) {
139
+ logger.trace({ jid }, 'injecting E2EE session')
140
+ const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid))
141
+ return parsedKeys.transaction(async () => {
142
+ await cipher.initOutgoing(session)
143
+ }, jid)
144
+ },
145
+ jidToSignalProtocolAddress(jid) {
146
+ return jidToSignalProtocolAddress(jid).toString()
147
+ },
148
+
149
+ // Optimized direct access to LID mapping store
150
+ lidMapping,
151
+
152
+ async validateSession(jid: string) {
153
+ try {
154
+ const addr = jidToSignalProtocolAddress(jid)
155
+ const session = await storage.loadSession(addr.toString())
156
+
157
+ if (!session) {
158
+ return { exists: false, reason: 'no session' }
159
+ }
160
+
161
+ if (!session.haveOpenSession()) {
162
+ return { exists: false, reason: 'no open session' }
163
+ }
164
+
165
+ return { exists: true }
166
+ } catch (error) {
167
+ return { exists: false, reason: 'validation error' }
168
+ }
169
+ },
170
+
171
+ async deleteSession(jids: string[]) {
172
+ if (!jids.length) return
173
+
174
+ // Convert JIDs to signal addresses and prepare for bulk deletion
175
+ const sessionUpdates: { [key: string]: null } = {}
176
+ jids.forEach(jid => {
177
+ const addr = jidToSignalProtocolAddress(jid)
178
+ sessionUpdates[addr.toString()] = null
179
+ })
180
+
181
+ // Single transaction for all deletions
182
+ return parsedKeys.transaction(async () => {
183
+ await auth.keys.set({ session: sessionUpdates })
184
+ }, `delete-${jids.length}-sessions`)
185
+ },
186
+
187
+ async migrateSession(
188
+ fromJid: string,
189
+ toJid: string
190
+ ): Promise<{ migrated: number; skipped: number; total: number }> {
191
+ // TODO: use usync to handle this entire mess
192
+ if (!fromJid || (!isLidUser(toJid) && !isHostedLidUser(toJid))) return { migrated: 0, skipped: 0, total: 0 }
193
+
194
+ // Only support PN to LID migration
195
+ if (!isPnUser(fromJid) && !isHostedPnUser(fromJid)) {
196
+ return { migrated: 0, skipped: 0, total: 1 }
197
+ }
198
+
199
+ const { user } = jidDecode(fromJid)!
200
+
201
+ logger.debug({ fromJid }, 'bulk device migration - loading all user devices')
202
+
203
+ // Get user's device list from storage
204
+ const { [user]: userDevices } = await parsedKeys.get('device-list', [user])
205
+ if (!userDevices) {
206
+ return { migrated: 0, skipped: 0, total: 0 }
207
+ }
208
+
209
+ const { device: fromDevice } = jidDecode(fromJid)!
210
+ const fromDeviceStr = fromDevice?.toString() || '0'
211
+ if (!userDevices.includes(fromDeviceStr)) {
212
+ userDevices.push(fromDeviceStr)
213
+ }
214
+
215
+ // Filter out cached devices before database fetch
216
+ const uncachedDevices = userDevices.filter(device => {
217
+ const deviceKey = `${user}.${device}`
218
+ return !migratedSessionCache.has(deviceKey)
219
+ })
220
+
221
+ // Bulk check session existence only for uncached devices
222
+ const deviceSessionKeys = uncachedDevices.map(device => `${user}.${device}`)
223
+ const existingSessions = await parsedKeys.get('session', deviceSessionKeys)
224
+
225
+ // Step 3: Convert existing sessions to JIDs (only migrate sessions that exist)
226
+ const deviceJids: string[] = []
227
+ for (const [sessionKey, sessionData] of Object.entries(existingSessions)) {
228
+ if (sessionData) {
229
+ // Session exists in storage
230
+ const deviceStr = sessionKey.split('.')[1]
231
+ if (!deviceStr) continue
232
+ const deviceNum = parseInt(deviceStr)
233
+ let jid = deviceNum === 0 ? `${user}@s.whatsapp.net` : `${user}:${deviceNum}@s.whatsapp.net`
234
+ if (deviceNum === 99) {
235
+ jid = `${user}:99@hosted`
236
+ }
237
+
238
+ deviceJids.push(jid)
239
+ }
240
+ }
241
+
242
+ logger.debug(
243
+ {
244
+ fromJid,
245
+ totalDevices: userDevices.length,
246
+ devicesWithSessions: deviceJids.length,
247
+ devices: deviceJids
248
+ },
249
+ 'bulk device migration complete - all user devices processed'
250
+ )
251
+
252
+ // Single transaction for all migrations
253
+ return parsedKeys.transaction(
254
+ async (): Promise<{ migrated: number; skipped: number; total: number }> => {
255
+ // Prepare migration operations with addressing metadata
256
+ type MigrationOp = {
257
+ fromJid: string
258
+ toJid: string
259
+ pnUser: string
260
+ lidUser: string
261
+ deviceId: number
262
+ fromAddr: libsignal.ProtocolAddress
263
+ toAddr: libsignal.ProtocolAddress
264
+ }
265
+
266
+ const migrationOps: MigrationOp[] = deviceJids.map(jid => {
267
+ const lidWithDevice = transferDevice(jid, toJid)
268
+ const fromDecoded = jidDecode(jid)!
269
+ const toDecoded = jidDecode(lidWithDevice)!
270
+
271
+ return {
272
+ fromJid: jid,
273
+ toJid: lidWithDevice,
274
+ pnUser: fromDecoded.user,
275
+ lidUser: toDecoded.user,
276
+ deviceId: fromDecoded.device || 0,
277
+ fromAddr: jidToSignalProtocolAddress(jid),
278
+ toAddr: jidToSignalProtocolAddress(lidWithDevice)
279
+ }
280
+ })
281
+
282
+ const totalOps = migrationOps.length
283
+ let migratedCount = 0
284
+
285
+ // Bulk fetch PN sessions - already exist (verified during device discovery)
286
+ const pnAddrStrings = Array.from(new Set(migrationOps.map(op => op.fromAddr.toString())))
287
+ const pnSessions = await parsedKeys.get('session', pnAddrStrings)
288
+
289
+ // Prepare bulk session updates (PN → LID migration + deletion)
290
+ const sessionUpdates: { [key: string]: Uint8Array | null } = {}
291
+
292
+ for (const op of migrationOps) {
293
+ const pnAddrStr = op.fromAddr.toString()
294
+ const lidAddrStr = op.toAddr.toString()
295
+
296
+ const pnSession = pnSessions[pnAddrStr]
297
+ if (pnSession) {
298
+ // Session exists (guaranteed from device discovery)
299
+ const fromSession = libsignal.SessionRecord.deserialize(pnSession)
300
+ if (fromSession.haveOpenSession()) {
301
+ // Queue for bulk update: copy to LID, delete from PN
302
+ sessionUpdates[lidAddrStr] = fromSession.serialize()
303
+ sessionUpdates[pnAddrStr] = null
304
+
305
+ migratedCount++
306
+ }
307
+ }
308
+ }
309
+
310
+ // Single bulk session update for all migrations
311
+ if (Object.keys(sessionUpdates).length > 0) {
312
+ await parsedKeys.set({ session: sessionUpdates })
313
+ logger.debug({ migratedSessions: migratedCount }, 'bulk session migration complete')
314
+
315
+ // Cache device-level migrations
316
+ for (const op of migrationOps) {
317
+ if (sessionUpdates[op.toAddr.toString()]) {
318
+ const deviceKey = `${op.pnUser}.${op.deviceId}`
319
+ migratedSessionCache.set(deviceKey, true)
320
+ }
321
+ }
322
+ }
323
+
324
+ const skippedCount = totalOps - migratedCount
325
+ return { migrated: migratedCount, skipped: skippedCount, total: totalOps }
326
+ },
327
+ `migrate-${deviceJids.length}-sessions-${jidDecode(toJid)?.user}`
328
+ )
329
+ }
330
+ }
331
+
332
+ return repository
333
+ }
334
+
335
+ const jidToSignalProtocolAddress = (jid: string): libsignal.ProtocolAddress => {
336
+ const decoded = jidDecode(jid)!
337
+ const { user, device, server, domainType } = decoded
338
+
339
+ if (!user) {
340
+ throw new Error(
341
+ `JID decoded but user is empty: "${jid}" -> user: "${user}", server: "${server}", device: ${device}`
342
+ )
343
+ }
344
+
345
+ const signalUser = domainType !== WAJIDDomains.WHATSAPP ? `${user}_${domainType}` : user
346
+ const finalDevice = device || 0
347
+
348
+ if (device === 99 && decoded.server !== 'hosted' && decoded.server !== 'hosted.lid') {
349
+ throw new Error('Unexpected non-hosted device JID with device 99. This ID seems invalid. ID:' + jid)
350
+ }
351
+
352
+ return new libsignal.ProtocolAddress(signalUser, finalDevice)
353
+ }
354
+
355
+ const jidToSignalSenderKeyName = (group: string, user: string): SenderKeyName => {
356
+ return new SenderKeyName(group, jidToSignalProtocolAddress(user))
357
+ }
358
+
359
+ function signalStorage(
360
+ { creds, keys }: SignalAuthState,
361
+ lidMapping: LIDMappingStore
362
+ ): SenderKeyStore & libsignal.SignalStorage {
363
+ // Shared function to resolve PN signal address to LID if mapping exists
364
+ const resolveLIDSignalAddress = async (id: string): Promise<string> => {
365
+ if (id.includes('.')) {
366
+ const [deviceId, device] = id.split('.')
367
+ const [user, domainType_] = deviceId!.split('_')
368
+ const domainType = parseInt(domainType_ || '0')
369
+
370
+ if (domainType === WAJIDDomains.LID || domainType === WAJIDDomains.HOSTED_LID) return id
371
+
372
+ const pnJid = `${user!}${device !== '0' ? `:${device}` : ''}@${domainType === WAJIDDomains.HOSTED ? 'hosted' : 's.whatsapp.net'}`
373
+
374
+ const lidForPN = await lidMapping.getLIDForPN(pnJid)
375
+ if (lidForPN) {
376
+ const lidAddr = jidToSignalProtocolAddress(lidForPN)
377
+ return lidAddr.toString()
378
+ }
379
+ }
380
+
381
+ return id
382
+ }
383
+
384
+ return {
385
+ loadSession: async (id: string) => {
386
+ try {
387
+ const wireJid = await resolveLIDSignalAddress(id)
388
+ const { [wireJid]: sess } = await keys.get('session', [wireJid])
389
+
390
+ if (sess) {
391
+ return libsignal.SessionRecord.deserialize(sess)
392
+ }
393
+ } catch (e) {
394
+ return null
395
+ }
396
+
397
+ return null
398
+ },
399
+ storeSession: async (id: string, session: libsignal.SessionRecord) => {
400
+ const wireJid = await resolveLIDSignalAddress(id)
401
+ await keys.set({ session: { [wireJid]: session.serialize() } })
402
+ },
403
+ isTrustedIdentity: () => {
404
+ return true // todo: implement
405
+ },
406
+ loadPreKey: async (id: number | string) => {
407
+ const keyId = id.toString()
408
+ const { [keyId]: key } = await keys.get('pre-key', [keyId])
409
+ if (key) {
410
+ return {
411
+ privKey: Buffer.from(key.private),
412
+ pubKey: Buffer.from(key.public)
413
+ }
414
+ }
415
+ },
416
+ removePreKey: (id: number) => keys.set({ 'pre-key': { [id]: null } }),
417
+ loadSignedPreKey: () => {
418
+ const key = creds.signedPreKey
419
+ return {
420
+ privKey: Buffer.from(key.keyPair.private),
421
+ pubKey: Buffer.from(key.keyPair.public)
422
+ }
423
+ },
424
+ loadSenderKey: async (senderKeyName: SenderKeyName) => {
425
+ const keyId = senderKeyName.toString()
426
+ const { [keyId]: key } = await keys.get('sender-key', [keyId])
427
+ if (key) {
428
+ return SenderKeyRecord.deserialize(key)
429
+ }
430
+
431
+ return new SenderKeyRecord()
432
+ },
433
+ storeSenderKey: async (senderKeyName: SenderKeyName, key: SenderKeyRecord) => {
434
+ const keyId = senderKeyName.toString()
435
+ const serialized = JSON.stringify(key.serialize())
436
+ await keys.set({ 'sender-key': { [keyId]: Buffer.from(serialized, 'utf-8') } })
437
+ },
438
+ getOurRegistrationId: () => creds.registrationId,
439
+ getOurIdentity: () => {
440
+ const { signedIdentityKey } = creds
441
+ return {
442
+ privKey: Buffer.from(signedIdentityKey.private),
443
+ pubKey: Buffer.from(generateSignalPubKey(signedIdentityKey.public))
444
+ }
445
+ }
446
+ }
447
+ }
@@ -0,0 +1,209 @@
1
+ import { LRUCache } from 'lru-cache'
2
+ import type { LIDMapping, SignalKeyStoreWithTransaction } from '../Types'
3
+ import type { ILogger } from '../Utils/logger'
4
+ import { isHostedPnUser, isLidUser, isPnUser, jidDecode, jidNormalizedUser, WAJIDDomains } from '../WABinary'
5
+
6
+ export class LIDMappingStore {
7
+ private readonly mappingCache = new LRUCache<string, string>({
8
+ ttl: 3 * 24 * 60 * 60 * 1000, // 7 days
9
+ ttlAutopurge: true,
10
+ updateAgeOnGet: true
11
+ })
12
+ private readonly keys: SignalKeyStoreWithTransaction
13
+ private readonly logger: ILogger
14
+
15
+ private pnToLIDFunc?: (jids: string[]) => Promise<LIDMapping[] | undefined>
16
+
17
+ constructor(
18
+ keys: SignalKeyStoreWithTransaction,
19
+ logger: ILogger,
20
+ pnToLIDFunc?: (jids: string[]) => Promise<LIDMapping[] | undefined>
21
+ ) {
22
+ this.keys = keys
23
+ this.pnToLIDFunc = pnToLIDFunc
24
+ this.logger = logger
25
+ }
26
+
27
+ /**
28
+ * Store LID-PN mapping - USER LEVEL
29
+ */
30
+ async storeLIDPNMappings(pairs: LIDMapping[]): Promise<void> {
31
+ // Validate inputs
32
+ const pairMap: { [_: string]: string } = {}
33
+ for (const { lid, pn } of pairs) {
34
+ if (!((isLidUser(lid) && isPnUser(pn)) || (isPnUser(lid) && isLidUser(pn)))) {
35
+ this.logger.warn(`Invalid LID-PN mapping: ${lid}, ${pn}`)
36
+ continue
37
+ }
38
+
39
+ const lidDecoded = jidDecode(lid)
40
+ const pnDecoded = jidDecode(pn)
41
+
42
+ if (!lidDecoded || !pnDecoded) return
43
+
44
+ const pnUser = pnDecoded.user
45
+ const lidUser = lidDecoded.user
46
+
47
+ let existingLidUser = this.mappingCache.get(`pn:${pnUser}`)
48
+ if (!existingLidUser) {
49
+ this.logger.trace(`Cache miss for PN user ${pnUser}; checking database`)
50
+ const stored = await this.keys.get('lid-mapping', [pnUser])
51
+ existingLidUser = stored[pnUser]
52
+ if (existingLidUser) {
53
+ // Update cache with database value
54
+ this.mappingCache.set(`pn:${pnUser}`, existingLidUser)
55
+ this.mappingCache.set(`lid:${existingLidUser}`, pnUser)
56
+ }
57
+ }
58
+
59
+ if (existingLidUser === lidUser) {
60
+ this.logger.debug({ pnUser, lidUser }, 'LID mapping already exists, skipping')
61
+ continue
62
+ }
63
+
64
+ pairMap[pnUser] = lidUser
65
+ }
66
+
67
+ this.logger.trace({ pairMap }, `Storing ${Object.keys(pairMap).length} pn mappings`)
68
+
69
+ await this.keys.transaction(async () => {
70
+ for (const [pnUser, lidUser] of Object.entries(pairMap)) {
71
+ await this.keys.set({
72
+ 'lid-mapping': {
73
+ [pnUser]: lidUser,
74
+ [`${lidUser}_reverse`]: pnUser
75
+ }
76
+ })
77
+
78
+ this.mappingCache.set(`pn:${pnUser}`, lidUser)
79
+ this.mappingCache.set(`lid:${lidUser}`, pnUser)
80
+ }
81
+ }, 'lid-mapping')
82
+ }
83
+
84
+ /**
85
+ * Get LID for PN - Returns device-specific LID based on user mapping
86
+ */
87
+ async getLIDForPN(pn: string): Promise<string | null> {
88
+ return (await this.getLIDsForPNs([pn]))?.[0]?.lid || null
89
+ }
90
+
91
+ async getLIDsForPNs(pns: string[]): Promise<LIDMapping[] | null> {
92
+ const usyncFetch: { [_: string]: number[] } = {}
93
+ // mapped from pn to lid mapping to prevent duplication in results later
94
+ const successfulPairs: { [_: string]: LIDMapping } = {}
95
+ for (const pn of pns) {
96
+ if (!isPnUser(pn) && !isHostedPnUser(pn)) continue
97
+
98
+ const decoded = jidDecode(pn)
99
+ if (!decoded) continue
100
+
101
+ // Check cache first for PN → LID mapping
102
+ const pnUser = decoded.user
103
+ let lidUser = this.mappingCache.get(`pn:${pnUser}`)
104
+
105
+ if (!lidUser) {
106
+ // Cache miss - check database
107
+ const stored = await this.keys.get('lid-mapping', [pnUser])
108
+ lidUser = stored[pnUser]
109
+
110
+ if (lidUser) {
111
+ this.mappingCache.set(`pn:${pnUser}`, lidUser)
112
+ this.mappingCache.set(`lid:${lidUser}`, pnUser)
113
+ } else {
114
+ this.logger.trace(`No LID mapping found for PN user ${pnUser}; batch getting from USync`)
115
+ const device = decoded.device || 0
116
+ let normalizedPn = jidNormalizedUser(pn)
117
+ if (isHostedPnUser(normalizedPn)) {
118
+ normalizedPn = `${pnUser}@s.whatsapp.net`
119
+ }
120
+
121
+ if (!usyncFetch[normalizedPn]) {
122
+ usyncFetch[normalizedPn] = [device]
123
+ } else {
124
+ usyncFetch[normalizedPn]?.push(device)
125
+ }
126
+
127
+ continue
128
+ }
129
+ }
130
+
131
+ lidUser = lidUser.toString()
132
+ if (!lidUser) {
133
+ this.logger.warn(`Invalid or empty LID user for PN ${pn}: lidUser = "${lidUser}"`)
134
+ return null
135
+ }
136
+
137
+ // Push the PN device ID to the LID to maintain device separation
138
+ const pnDevice = decoded.device !== undefined ? decoded.device : 0
139
+ const deviceSpecificLid = `${lidUser}${!!pnDevice ? `:${pnDevice}` : ``}@${decoded.server === 'hosted' ? 'hosted.lid' : 'lid'}`
140
+
141
+ this.logger.trace(`getLIDForPN: ${pn} → ${deviceSpecificLid} (user mapping with device ${pnDevice})`)
142
+ successfulPairs[pn] = { lid: deviceSpecificLid, pn }
143
+ }
144
+
145
+ if (Object.keys(usyncFetch).length > 0) {
146
+ const result = await this.pnToLIDFunc?.(Object.keys(usyncFetch)) // this function already adds LIDs to mapping
147
+ if (result && result.length > 0) {
148
+ await this.storeLIDPNMappings(result)
149
+ for (const pair of result) {
150
+ const pnDecoded = jidDecode(pair.pn)
151
+ const pnUser = pnDecoded?.user
152
+ if (!pnUser) continue
153
+ const lidUser = jidDecode(pair.lid)?.user
154
+ if (!lidUser) continue
155
+
156
+ for (const device of usyncFetch[pair.pn]!) {
157
+ const deviceSpecificLid = `${lidUser}${!!device ? `:${device}` : ``}@${device === 99 ? 'hosted.lid' : 'lid'}`
158
+
159
+ this.logger.trace(
160
+ `getLIDForPN: USYNC success for ${pair.pn} → ${deviceSpecificLid} (user mapping with device ${device})`
161
+ )
162
+
163
+ const deviceSpecificPn = `${pnUser}${!!device ? `:${device}` : ``}@${device === 99 ? 'hosted' : 's.whatsapp.net'}`
164
+
165
+ successfulPairs[deviceSpecificPn] = { lid: deviceSpecificLid, pn: deviceSpecificPn }
166
+ }
167
+ }
168
+ } else {
169
+ return null
170
+ }
171
+ }
172
+
173
+ return Object.values(successfulPairs)
174
+ }
175
+
176
+ /**
177
+ * Get PN for LID - USER LEVEL with device construction
178
+ */
179
+ async getPNForLID(lid: string): Promise<string | null> {
180
+ if (!isLidUser(lid)) return null
181
+
182
+ const decoded = jidDecode(lid)
183
+ if (!decoded) return null
184
+
185
+ // Check cache first for LID → PN mapping
186
+ const lidUser = decoded.user
187
+ let pnUser = this.mappingCache.get(`lid:${lidUser}`)
188
+
189
+ if (!pnUser || typeof pnUser !== 'string') {
190
+ // Cache miss - check database
191
+ const stored = await this.keys.get('lid-mapping', [`${lidUser}_reverse`])
192
+ pnUser = stored[`${lidUser}_reverse`]
193
+
194
+ if (!pnUser || typeof pnUser !== 'string') {
195
+ this.logger.trace(`No reverse mapping found for LID user: ${lidUser}`)
196
+ return null
197
+ }
198
+
199
+ this.mappingCache.set(`lid:${lidUser}`, pnUser)
200
+ }
201
+
202
+ // Construct device-specific PN JID
203
+ const lidDevice = decoded.device !== undefined ? decoded.device : 0
204
+ const pnJid = `${pnUser}:${lidDevice}@${decoded.domainType === WAJIDDomains.HOSTED_LID ? 'hosted' : 's.whatsapp.net'}`
205
+
206
+ this.logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`)
207
+ return pnJid
208
+ }
209
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types'
2
+ export * from './websocket'
@@ -0,0 +1,22 @@
1
+ import { EventEmitter } from 'events'
2
+ import { URL } from 'url'
3
+ import type { SocketConfig } from '../../Types'
4
+
5
+ export abstract class AbstractSocketClient extends EventEmitter {
6
+ abstract get isOpen(): boolean
7
+ abstract get isClosed(): boolean
8
+ abstract get isClosing(): boolean
9
+ abstract get isConnecting(): boolean
10
+
11
+ constructor(
12
+ public url: URL,
13
+ public config: SocketConfig
14
+ ) {
15
+ super()
16
+ this.setMaxListeners(0)
17
+ }
18
+
19
+ abstract connect(): void
20
+ abstract close(): void
21
+ abstract send(str: Uint8Array | string, cb?: (err?: Error) => void): boolean
22
+ }