@badzz88/baileys 8.5.7 → 8.5.8

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 (142) hide show
  1. package/README.md +337 -1055
  2. package/WAProto/WAProto.proto +284 -264
  3. package/WAProto/index.js +5 -5
  4. package/engine-requirements.js +7 -15
  5. package/lib/Defaults/index.js +130 -187
  6. package/lib/Signal/Group/ciphertext-message.js +11 -14
  7. package/lib/Signal/Group/group-session-builder.js +29 -91
  8. package/lib/Signal/Group/group_cipher.js +81 -88
  9. package/lib/Signal/Group/index.js +12 -136
  10. package/lib/Signal/Group/keyhelper.js +15 -70
  11. package/lib/Signal/Group/sender-chain-key.js +25 -31
  12. package/lib/Signal/Group/sender-key-distribution-message.js +62 -65
  13. package/lib/Signal/Group/sender-key-message.js +65 -68
  14. package/lib/Signal/Group/sender-key-name.js +41 -43
  15. package/lib/Signal/Group/sender-key-record.js +40 -43
  16. package/lib/Signal/Group/sender-key-state.js +83 -96
  17. package/lib/Signal/Group/sender-message-key.js +25 -29
  18. package/lib/Signal/libsignal.js +425 -453
  19. package/lib/Signal/lid-mapping.js +276 -259
  20. package/lib/Socket/Client/index.js +3 -30
  21. package/lib/Socket/Client/types.js +10 -12
  22. package/lib/Socket/Client/websocket.js +53 -61
  23. package/lib/Socket/business.js +379 -422
  24. package/lib/Socket/chats.js +1193 -1816
  25. package/lib/Socket/communities.js +431 -549
  26. package/lib/Socket/graphql.js +672 -612
  27. package/lib/Socket/groups.js +374 -764
  28. package/lib/Socket/index.js +17 -38
  29. package/lib/Socket/interop.js +417 -503
  30. package/lib/Socket/messages-recv.js +1915 -2441
  31. package/lib/Socket/messages-send.js +1213 -1630
  32. package/lib/Socket/mex.js +9 -15
  33. package/lib/Socket/newsletter.js +747 -808
  34. package/lib/Socket/privacy.js +310 -441
  35. package/lib/Socket/socket.js +1025 -1022
  36. package/lib/Socket/username.js +8 -15
  37. package/lib/Store/index.js +10 -36
  38. package/lib/Store/make-cache-manager-store.js +80 -85
  39. package/lib/Store/make-in-memory-store.js +231 -591
  40. package/lib/Store/make-ordered-dictionary.js +72 -78
  41. package/lib/Store/object-repository.js +28 -25
  42. package/lib/Types/Auth.js +2 -38
  43. package/lib/Types/Bussines.js +2 -2
  44. package/lib/Types/Call.js +2 -2
  45. package/lib/Types/Chat.js +8 -4
  46. package/lib/Types/Contact.js +2 -2
  47. package/lib/Types/Events.js +2 -2
  48. package/lib/Types/GroupMetadata.js +2 -2
  49. package/lib/Types/Label.js +24 -26
  50. package/lib/Types/LabelAssociation.js +6 -8
  51. package/lib/Types/Message.js +11 -95
  52. package/lib/Types/Mex.js +111 -112
  53. package/lib/Types/Product.js +2 -2
  54. package/lib/Types/Signal.js +2 -2
  55. package/lib/Types/Socket.js +3 -2
  56. package/lib/Types/State.js +56 -70
  57. package/lib/Types/USync.js +2 -2
  58. package/lib/Types/index.js +26 -55
  59. package/lib/Utils/auth-utils.js +288 -292
  60. package/lib/Utils/browser-utils.js +47 -113
  61. package/lib/Utils/business.js +224 -240
  62. package/lib/Utils/chat-utils.js +869 -1205
  63. package/lib/Utils/companion-reg-client-utils.js +34 -41
  64. package/lib/Utils/crypto.js +107 -144
  65. package/lib/Utils/decode-wa-message.js +308 -518
  66. package/lib/Utils/event-buffer.js +611 -596
  67. package/lib/Utils/generics.js +355 -515
  68. package/lib/Utils/history.js +134 -244
  69. package/lib/Utils/identity-change-handler.js +49 -51
  70. package/lib/Utils/index.js +23 -57
  71. package/lib/Utils/link-preview.js +77 -135
  72. package/lib/Utils/logger.js +3 -9
  73. package/lib/Utils/lt-hash.js +6 -37
  74. package/lib/Utils/make-mutex.js +33 -36
  75. package/lib/Utils/message-composer.js +233 -439
  76. package/lib/Utils/message-retry-manager.js +260 -265
  77. package/lib/Utils/messages-media.js +829 -855
  78. package/lib/Utils/messages.js +961 -2528
  79. package/lib/Utils/noise-handler.js +200 -195
  80. package/lib/Utils/offline-node-processor.js +33 -35
  81. package/lib/Utils/pre-key-manager.js +102 -103
  82. package/lib/Utils/process-message.js +560 -978
  83. package/lib/Utils/reporting-utils.js +253 -257
  84. package/lib/Utils/signal.js +195 -218
  85. package/lib/Utils/stanza-ack.js +29 -31
  86. package/lib/Utils/sticker.js +109 -144
  87. package/lib/Utils/sync-action-utils.js +45 -50
  88. package/lib/Utils/tc-token-utils.js +139 -137
  89. package/lib/Utils/use-multi-file-auth-state.js +109 -109
  90. package/lib/Utils/validate-connection.js +202 -218
  91. package/lib/WABinary/constants.js +1299 -1302
  92. package/lib/WABinary/decode.js +262 -343
  93. package/lib/WABinary/encode.js +4 -12
  94. package/lib/WABinary/generic-utils.js +187 -223
  95. package/lib/WABinary/index.js +6 -33
  96. package/lib/WABinary/jid-utils.js +88 -351
  97. package/lib/WABinary/types.js +2 -2
  98. package/lib/WAM/BinaryInfo.js +9 -12
  99. package/lib/WAM/constants.js +22853 -39486
  100. package/lib/WAM/encode.js +140 -132
  101. package/lib/WAM/index.js +4 -31
  102. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +21 -23
  103. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +9 -9
  104. package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -7
  105. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +10 -10
  106. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +7 -7
  107. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +11 -12
  108. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +4 -5
  109. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +7 -7
  110. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +7 -8
  111. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -7
  112. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +8 -8
  113. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +7 -7
  114. package/lib/WAUSync/Protocols/index.js +12 -39
  115. package/lib/WAUSync/USyncQuery.js +67 -49
  116. package/lib/WAUSync/USyncUser.js +11 -5
  117. package/lib/WAUSync/index.js +3 -31
  118. package/lib/index.js +15 -48
  119. package/package.json +129 -99
  120. package/LICENSE +0 -21
  121. package/lib/Defaults/phonenumber-mcc.json +0 -223
  122. package/lib/Socket/aigroups.js +0 -240
  123. package/lib/Socket/interactive-handler.js +0 -522
  124. package/lib/Socket/managed-account.js +0 -183
  125. package/lib/Socket/registration.js +0 -427
  126. package/lib/Socket/text-router.js +0 -83
  127. package/lib/Types/Newsletter.js +0 -121
  128. package/lib/Utils/command-loader.d.ts +0 -31
  129. package/lib/Utils/command-loader.js +0 -100
  130. package/lib/Utils/consumer-application.js +0 -107
  131. package/lib/Utils/curve25519-js.js +0 -275
  132. package/lib/Utils/group-history.js +0 -60
  133. package/lib/Utils/jid-display-normalization.js +0 -218
  134. package/lib/Utils/meta-ai-msmsg.js +0 -262
  135. package/lib/Utils/native-bridge.js +0 -82
  136. package/lib/Utils/session-pool.d.ts +0 -16
  137. package/lib/Utils/session-pool.js +0 -94
  138. package/lib/Utils/sticker.d.ts +0 -17
  139. package/lib/Utils/view-once-cache.d.ts +0 -9
  140. package/lib/Utils/view-once-cache.js +0 -79
  141. package/lib/Utils/voip-rekey.js +0 -22
  142. package/lib/antiban.js +0 -4637
@@ -1,24 +1,25 @@
1
- 'use strict'
2
- Object.defineProperty(exports, '__esModule', { value: true })
3
- exports.makeInteropSocket = void 0
4
-
5
- const { getBinaryNodeChild, getBinaryNodeChildren, S_WHATSAPP_NET } = require('../WABinary')
6
- const { executeWMexQuery } = require('./mex')
1
+ import { getBinaryNodeChild, getBinaryNodeChildren, S_WHATSAPP_NET } from '../WABinary/index.js'
2
+ import { executeWMexQuery } from './mex.js'
7
3
 
4
+ /**
5
+ * w:mex query IDs for interop group and privacy operations.
6
+ *
7
+ * Source: whatsapp-android-mex_client_persist_ids.json (WA 2.26.26.4 APK assets)
8
+ */
8
9
  const INTEROP_MEX_QUERY_IDS = {
9
- CREATE_GROUP: '25726817620301612', // GroupsCreateInteropGroup
10
- LEAVE_GROUP: '25346167795013270', // LeaveInteropGroup
11
- ADD_PARTICIPANTS: '25732168276369452', // AddParticipantsToInteropGroup
12
- QUERY_GROUP_INFO: '32734144032867936', // QueryInteropGroupInfo
13
- PRIVACY_SETTINGS_QUERY: '24849123668112656', // InteropPrivacySettingsQuery
14
- PRIVACY_SETTINGS_UPDATE: '25421856497452764', // InteropPrivacySettingsUpdate
15
- PRIVACY_SETTINGS_WITH_CONTACT_LIST: '24913399124998600' // InteropPrivacySettingWithContactListUpdate
10
+ CREATE_GROUP: '25726817620301611', // GroupsCreateInteropGroup
11
+ LEAVE_GROUP: '25346167795013271', // LeaveInteropGroup
12
+ ADD_PARTICIPANTS: '25732168276369451', // AddParticipantsToInteropGroup
13
+ QUERY_GROUP_INFO: '32734144032867938', // QueryInteropGroupInfo
14
+ PRIVACY_SETTINGS_QUERY: '24849123668112654', // InteropPrivacySettingsQuery
15
+ PRIVACY_SETTINGS_UPDATE: '25421856497452763', // InteropPrivacySettingsUpdate
16
+ PRIVACY_SETTINGS_WITH_CONTACT_LIST: '24913399124998598' // InteropPrivacySettingWithContactListUpdate
16
17
  }
17
18
 
18
19
  /**
19
20
  * Known integrator IDs (assigned by WhatsApp).
20
21
  * BirdyChat → identifier_type="email"
21
- * Haiket → identifier_type="pn"
22
+ * Haiket → identifier_type="pn"
22
23
  */
23
24
  const INTEGRATOR_BIRDYCHAT = 12
24
25
  const INTEGRATOR_HAIKET = 13
@@ -34,516 +35,429 @@ const TOS_RESULT_ACCEPTED = '160'
34
35
  /** Maximum users per batch lookup (enforced server-side too). */
35
36
  const INTEROP_BATCH_MAX = 256
36
37
 
37
- const makeInteropSocket = sock => {
38
- const { query, generateMessageTag, logger, signalRepository } = sock
38
+ export const makeInteropSocket = sock => {
39
+ const { query, generateMessageTag, logger, signalRepository } = sock
39
40
 
40
- const mexQuery = (variables, queryId, dataPath) =>
41
- executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
41
+ const mexQuery = (variables, queryId, dataPath) =>
42
+ executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
42
43
 
43
- /**
44
- * Fetch all available interop integrators from the server.
45
- * Each integrator carries: id, name, status, icon, identifierType,
46
- * optedIn, features.groupMessaging.
47
- * status can be "active" | "onboarding" | "removed".
48
- */
49
- const fetchIntegrators = async () => {
50
- const result = await query({
51
- tag: 'iq',
52
- attrs: {
53
- type: 'get',
54
- xmlns: 'w:interop',
55
- to: S_WHATSAPP_NET
56
- },
57
- content: [{ tag: 'integrator', attrs: { fetch: 'all' } }]
58
- })
59
- const listNode = getBinaryNodeChild(result, 'integrator_list')
60
- if (!listNode) return []
61
- const globalOptedIn = listNode.attrs?.opted_in === 'true'
62
- return getBinaryNodeChildren(listNode, 'integrator').map(node => {
63
- const featuresNode = getBinaryNodeChild(node, 'features')
64
- return {
65
- id: parseInt(node.attrs.id),
66
- name: node.attrs.name,
67
- // "active" | "onboarding" | "removed"
68
- status: node.attrs.status,
69
- icon: node.attrs.icon,
70
- // "email" | "pn" | "username"
71
- identifierType: node.attrs.identifier_type,
72
- // Whether we are already opted-in to this integrator
73
- optedIn: node.attrs.opted_in === 'true' || globalOptedIn,
74
- features: {
75
- groupMessaging: featuresNode?.attrs?.group_messaging === 'true'
76
- }
77
- }
78
- })
79
- }
44
+ /**
45
+ * Fetch all available interop integrators from the server.
46
+ */
47
+ const fetchIntegrators = async () => {
48
+ const result = await query({
49
+ tag: 'iq',
50
+ attrs: {
51
+ type: 'get',
52
+ xmlns: 'w:interop',
53
+ to: S_WHATSAPP_NET
54
+ },
55
+ content: [{ tag: 'integrator', attrs: { fetch: 'all' } }]
56
+ })
57
+ const listNode = getBinaryNodeChild(result, 'integrator_list')
58
+ if (!listNode) return []
59
+ const globalOptedIn = listNode.attrs?.opted_in === 'true'
60
+ return getBinaryNodeChildren(listNode, 'integrator').map(node => {
61
+ const featuresNode = getBinaryNodeChild(node, 'features')
62
+ return {
63
+ id: parseInt(node.attrs.id, 10),
64
+ name: node.attrs.name,
65
+ // "active" | "onboarding" | "removed"
66
+ status: node.attrs.status,
67
+ icon: node.attrs.icon,
68
+ // "email" | "pn" | "username"
69
+ identifierType: node.attrs.identifier_type,
70
+ // Whether we are already opted-in to this integrator
71
+ optedIn: node.attrs.opted_in === 'true' || globalOptedIn,
72
+ features: {
73
+ groupMessaging: featuresNode?.attrs?.group_messaging === 'true'
74
+ }
75
+ }
76
+ })
77
+ }
80
78
 
81
- /** Send a single TOS trackable item (shown or accepted). */
82
- const sendTOSTrackable = async (id, result) => {
83
- await query({
84
- tag: 'iq',
85
- attrs: { to: S_WHATSAPP_NET, type: 'set', xmlns: 'tos' },
86
- content: [{ tag: 'trackable', attrs: { id, result } }]
87
- })
88
- }
79
+ /** Send a single TOS trackable item (shown or accepted). */
80
+ const sendTOSTrackable = async (id, result) => {
81
+ await query({
82
+ tag: 'iq',
83
+ attrs: { to: S_WHATSAPP_NET, type: 'set', xmlns: 'tos' },
84
+ content: [{ tag: 'trackable', attrs: { id, result } }]
85
+ })
86
+ }
89
87
 
90
- /**
91
- * Accept TOS for interop.
92
- * Sends two trackable items: shown (105) then accepted (160).
93
- * Must be called before opting in.
94
- */
95
- const acceptInteropTOS = async () => {
96
- await sendTOSTrackable(TOS_TRACKABLE_ID, TOS_RESULT_SHOWN)
97
- await sendTOSTrackable(TOS_TRACKABLE_ID, TOS_RESULT_ACCEPTED)
98
- }
88
+ /**
89
+ * Accept TOS for interop.
90
+ * Sends two trackable items: shown (105) then accepted (160).
91
+ */
92
+ const acceptInteropTOS = async () => {
93
+ await sendTOSTrackable(TOS_TRACKABLE_ID, TOS_RESULT_SHOWN)
94
+ await sendTOSTrackable(TOS_TRACKABLE_ID, TOS_RESULT_ACCEPTED)
95
+ }
99
96
 
100
- /**
101
- * Opt in to a list of integrators (by numeric ID).
102
- * Defaults to both known integrators (BirdyChat + Haiket).
103
- */
104
- const optInIntegrators = async (integratorIds = [INTEGRATOR_BIRDYCHAT, INTEGRATOR_HAIKET]) => {
105
- await query({
106
- tag: 'iq',
107
- attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
108
- content: [
109
- {
110
- tag: 'opt_in_integrators',
111
- attrs: {},
112
- content: [
113
- {
114
- tag: 'integrator_list',
115
- attrs: {},
116
- content: integratorIds.map(id => ({
117
- tag: 'integrator',
118
- attrs: { id: id.toString() }
119
- }))
120
- }
121
- ]
122
- }
123
- ]
124
- })
125
- }
97
+ /**
98
+ * Opt in to a list of integrators (by numeric ID).
99
+ */
100
+ const optInIntegrators = async (integratorIds = [INTEGRATOR_BIRDYCHAT, INTEGRATOR_HAIKET]) => {
101
+ await query({
102
+ tag: 'iq',
103
+ attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
104
+ content: [
105
+ {
106
+ tag: 'opt_integrators',
107
+ attrs: {},
108
+ content: [
109
+ {
110
+ tag: 'integrator_list',
111
+ attrs: {},
112
+ content: integratorIds.map(id => ({
113
+ tag: 'integrator',
114
+ attrs: { id: id.toString() }
115
+ }))
116
+ }
117
+ ]
118
+ }
119
+ ]
120
+ })
121
+ }
126
122
 
127
- /**
128
- * Opt out of a list of integrators (by numeric ID).
129
- * Mirror of optInIntegrators — uses <opt_out_integrators>.
130
- */
131
- const optOutIntegrators = async (integratorIds = [INTEGRATOR_BIRDYCHAT, INTEGRATOR_HAIKET]) => {
132
- await query({
133
- tag: 'iq',
134
- attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
135
- content: [
136
- {
137
- tag: 'opt_out_integrators',
138
- attrs: {},
139
- content: [
140
- {
141
- tag: 'integrator_list',
142
- attrs: {},
143
- content: integratorIds.map(id => ({
144
- tag: 'integrator',
145
- attrs: { id: id.toString() }
146
- }))
147
- }
148
- ]
149
- }
150
- ]
151
- })
152
- }
123
+ /**
124
+ * Opt out of a list of integrators (by numeric ID).
125
+ */
126
+ const optOutIntegrators = async (integratorIds = [INTEGRATOR_BIRDYCHAT, INTEGRATOR_HAIKET]) => {
127
+ await query({
128
+ tag: 'iq',
129
+ attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
130
+ content: [
131
+ {
132
+ tag: 'opt_out_integrators',
133
+ attrs: {},
134
+ content: [
135
+ {
136
+ tag: 'integrator_list',
137
+ attrs: {},
138
+ content: integratorIds.map(id => ({
139
+ tag: 'integrator',
140
+ attrs: { id: id.toString() }
141
+ }))
142
+ }
143
+ ]
144
+ }
145
+ ]
146
+ })
147
+ }
153
148
 
154
- /**
155
- * Resolve one or more interop users by external ID in a single IQ.
156
- * Each entry: { externalId, integratorId }
157
- * - BirdyChat (12): externalId = email address
158
- * - Haiket (13): externalId = phone number string (e.g. "19146088152")
159
- *
160
- * Max 256 entries per call (server limit).
161
- *
162
- * Returns an array of results:
163
- * { jid, externalId, normalizedExternalId, integratorId } on success
164
- * { externalId, integratorId, error: { code, text } } on failure
165
- */
166
- const resolveInteropUsers = async users => {
167
- if (!users || users.length === 0) return []
168
- if (users.length > INTEROP_BATCH_MAX) {
169
- throw new Error(`resolveInteropUsers: max ${INTEROP_BATCH_MAX} users per request`)
170
- }
171
- const result = await query({
172
- tag: 'iq',
173
- attrs: { type: 'get', xmlns: 'w:interop', to: S_WHATSAPP_NET },
174
- content: [
175
- {
176
- tag: 'users',
177
- attrs: {},
178
- content: users.map(({ externalId, integratorId }) => ({
179
- tag: 'user',
180
- attrs: {
181
- external_id: externalId,
182
- integrator_id: integratorId.toString()
183
- }
184
- }))
185
- }
186
- ]
187
- })
188
- const usersNode = getBinaryNodeChild(result, 'users')
189
- if (!usersNode) return []
190
- return getBinaryNodeChildren(usersNode, 'user').map(userNode => {
191
- const errorNode = getBinaryNodeChild(userNode, 'error')
192
- if (errorNode) {
193
- return {
194
- externalId: userNode.attrs.external_id,
195
- integratorId: parseInt(userNode.attrs.integrator_id),
196
- error: {
197
- code: parseInt(errorNode.attrs.code),
198
- text: errorNode.attrs.text
199
- }
200
- }
201
- }
202
- return {
203
- jid: userNode.attrs.jid,
204
- externalId: userNode.attrs.external_id,
205
- normalizedExternalId: userNode.attrs.normalized_external_id,
206
- integratorId: parseInt(userNode.attrs.integrator_id)
207
- }
208
- })
209
- }
149
+ /**
150
+ * Resolve one or more interop users by external ID in a single IQ.
151
+ */
152
+ const resolveInteropUsers = async users => {
153
+ if (!users || users.length === 0) return []
154
+ if (users.length > INTEROP_BATCH_MAX) {
155
+ throw new Error(`resolveInteropUsers: max ${INTEROP_BATCH_MAX} users per request`)
156
+ }
157
+ const result = await query({
158
+ tag: 'iq',
159
+ attrs: { type: 'get', xmlns: 'w:interop', to: S_WHATSAPP_NET },
160
+ content: [
161
+ {
162
+ tag: 'users',
163
+ attrs: {},
164
+ content: users.map(({ externalId, integratorId }) => ({
165
+ tag: 'user',
166
+ attrs: {
167
+ external_id: externalId,
168
+ integrator_id: integratorId.toString()
169
+ }
170
+ }))
171
+ }
172
+ ]
173
+ })
174
+ const usersNode = getBinaryNodeChild(result, 'users')
175
+ if (!usersNode) return []
176
+ return getBinaryNodeChildren(usersNode, 'user').map(userNode => {
177
+ const errorNode = getBinaryNodeChild(userNode, 'error')
178
+ if (errorNode) {
179
+ return {
180
+ externalId: userNode.attrs.external_id,
181
+ integratorId: parseInt(userNode.attrs.integrator_id, 10),
182
+ error: {
183
+ code: parseInt(errorNode.attrs.code, 10),
184
+ text: errorNode.attrs.text
185
+ }
186
+ }
187
+ }
188
+ return {
189
+ jid: userNode.attrs.jid,
190
+ externalId: userNode.attrs.external_id,
191
+ normalizedExternalId: userNode.attrs.normalized_external_id,
192
+ integratorId: parseInt(userNode.attrs.integrator_id, 10)
193
+ }
194
+ })
195
+ }
210
196
 
211
- /**
212
- * Convenience wrapper: resolve a single interop user.
213
- * Returns the result object or null.
214
- */
215
- const resolveInteropUser = async (externalId, integratorId) => {
216
- const results = await resolveInteropUsers([{ externalId, integratorId }])
217
- return results[0] ?? null
218
- }
197
+ /**
198
+ * Convenience wrapper: resolve a single interop user.
199
+ */
200
+ const resolveInteropUser = async (externalId, integratorId) => {
201
+ const results = await resolveInteropUsers([{ externalId, integratorId }])
202
+ return results[0]?? null
203
+ }
219
204
 
220
- /**
221
- * Get reachability settings for interop contacts.
222
- * Returns the raw <reachability_settings> node content.
223
- * WA uses this as the interop presence/subscription mechanism.
224
- */
225
- const getReachabilitySettings = async () => {
226
- const result = await query({
227
- tag: 'iq',
228
- attrs: { type: 'get', xmlns: 'w:interop', to: S_WHATSAPP_NET },
229
- content: [{ tag: 'reachability_settings', attrs: {} }]
230
- })
231
- const settingsNode = getBinaryNodeChild(result, 'reachability_settings')
232
- if (!settingsNode) return null
233
- return {
234
- enabled: settingsNode.attrs?.enabled,
235
- users: getBinaryNodeChildren(settingsNode, 'user').map(n => ({
236
- externalId: n.attrs.external_id,
237
- integratorId: parseInt(n.attrs.integrator_id),
238
- jid: n.attrs.jid
239
- }))
240
- }
241
- }
205
+ /**
206
+ * Get reachability settings for interop contacts.
207
+ */
208
+ const getReachabilitySettings = async () => {
209
+ const result = await query({
210
+ tag: 'iq',
211
+ attrs: { type: 'get', xmlns: 'w:interop', to: S_WHATSAPP_NET },
212
+ content: [{ tag: 'reachability_settings', attrs: {} }]
213
+ })
214
+ const settingsNode = getBinaryNodeChild(result, 'reachability_settings')
215
+ if (!settingsNode) return null
216
+ return {
217
+ enabled: settingsNode.attrs?.enabled,
218
+ users: getBinaryNodeChildren(settingsNode, 'user').map(n => ({
219
+ externalId: n.attrs.external_id,
220
+ integratorId: parseInt(n.attrs.integrator_id, 10),
221
+ jid: n.attrs.jid
222
+ }))
223
+ }
224
+ }
242
225
 
243
- /**
244
- * Set reachability settings (subscribe to presence) for interop contacts.
245
- * users: array of { externalId, integratorId }
246
- * enabled: "true" | "false"
247
- */
248
- const setReachabilitySettings = async (users, enabled = 'true') => {
249
- await query({
250
- tag: 'iq',
251
- attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
252
- content: [
253
- {
254
- tag: 'reachability_settings',
255
- attrs: { enabled },
256
- content: users.map(({ externalId, integratorId }) => ({
257
- tag: 'user',
258
- attrs: {
259
- external_id: externalId,
260
- integrator_id: integratorId.toString()
261
- }
262
- }))
263
- }
264
- ]
265
- })
266
- }
226
+ /**
227
+ * Set reachability settings (subscribe to presence) for interop contacts.
228
+ */
229
+ const setReachabilitySettings = async (users, enabled = 'true') => {
230
+ await query({
231
+ tag: 'iq',
232
+ attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
233
+ content: [
234
+ {
235
+ tag: 'reachability_settings',
236
+ attrs: { enabled },
237
+ content: users.map(({ externalId, integratorId }) => ({
238
+ tag: 'user',
239
+ attrs: {
240
+ external_id: externalId,
241
+ integrator_id: integratorId.toString()
242
+ }
243
+ }))
244
+ }
245
+ ]
246
+ })
247
+ }
267
248
 
268
- /**
269
- * Block or unblock an interop user via the w:interop blocklist.
270
- * Different from regular WA block (xmlns=blocklist).
271
- */
272
- const updateInteropBlockStatus = async (jid, action) => {
273
- await query({
274
- tag: 'iq',
275
- attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
276
- content: [
277
- {
278
- tag: 'blocklist',
279
- attrs: {},
280
- content: [{ tag: 'item', attrs: { action, jid } }]
281
- }
282
- ]
283
- })
284
- }
249
+ /**
250
+ * Block or unblock an interop user via the w:interop blocklist.
251
+ */
252
+ const updateInteropBlockStatus = async (jid, action) => {
253
+ await query({
254
+ tag: 'iq',
255
+ attrs: { type: 'set', xmlns: 'w:interop', to: S_WHATSAPP_NET },
256
+ content: [
257
+ {
258
+ tag: 'blocklist',
259
+ attrs: {},
260
+ content: [{ tag: 'item', attrs: { action, jid } }]
261
+ }
262
+ ]
263
+ })
264
+ }
285
265
 
286
- const blockInteropUser = jid => updateInteropBlockStatus(jid, 'block')
287
- const unblockInteropUser = jid => updateInteropBlockStatus(jid, 'unblock')
266
+ const blockInteropUser = jid => updateInteropBlockStatus(jid, 'block')
267
+ const unblockInteropUser = jid => updateInteropBlockStatus(jid, 'unblock')
288
268
 
289
- /**
290
- * Report an interop contact as spam.
291
- * spam_flow="account_info_block" = block + report (WA Web default).
292
- */
293
- const reportInteropSpam = async (jid, spamFlow = 'account_info_block') => {
294
- await query({
295
- tag: 'iq',
296
- attrs: { type: 'set', xmlns: 'spam', to: S_WHATSAPP_NET },
297
- content: [{ tag: 'spam_list', attrs: { jid, spam_flow: spamFlow } }]
298
- })
299
- }
269
+ /**
270
+ * Report an interop contact as spam.
271
+ */
272
+ const reportInteropSpam = async (jid, spamFlow = 'account_info_block') => {
273
+ await query({
274
+ tag: 'iq',
275
+ attrs: { type: 'set', xmlns: 'spam', to: S_WHATSAPP_NET },
276
+ content: [{ tag: 'spam_list', attrs: { jid, spam_flow: spamFlow } }]
277
+ })
278
+ }
300
279
 
301
- /**
302
- * Mark an interop JID as trusted_contact in privacy tokens.
303
- * WA calls this automatically after the first outgoing message.
304
- */
305
- const trustInteropContact = async jid => {
306
- const t = Math.floor(Date.now() / 1000).toString()
307
- await query({
308
- tag: 'iq',
309
- attrs: { to: S_WHATSAPP_NET, xmlns: 'privacy', type: 'set' },
310
- content: [
311
- {
312
- tag: 'tokens',
313
- attrs: {},
314
- content: [{ tag: 'token', attrs: { jid, type: 'trusted_contact', t } }]
315
- }
316
- ]
317
- })
318
- }
280
+ /**
281
+ * Mark an interop JID as trusted_contact in privacy tokens.
282
+ */
283
+ const trustInteropContact = async jid => {
284
+ const t = Math.floor(Date.now() / 1000).toString()
285
+ await query({
286
+ tag: 'iq',
287
+ attrs: { to: S_WHATSAPP_NET, xmlns: 'privacy', type: 'set' },
288
+ content: [
289
+ {
290
+ tag: 'tokens',
291
+ attrs: {},
292
+ content: [{ tag: 'token', attrs: { jid, type: 'trusted_contact', t } }]
293
+ }
294
+ ]
295
+ })
296
+ }
319
297
 
320
- /**
321
- * Full interop initialization sequence matching WA Web:
322
- * 1. Fetch available integrators
323
- * 2. Accept TOS (shown 105 + accepted 160)
324
- * 3. Opt-in to all active/onboarding integrators
325
- *
326
- * Silently tolerates TOS/opt-in errors.
327
- * Returns the full integrator list.
328
- */
329
- const initInterop = async () => {
330
- let integrators
331
- try {
332
- integrators = await fetchIntegrators()
333
- } catch (err) {
334
- logger.warn({ err }, 'interop: failed to fetch integrators')
335
- return []
336
- }
337
- const toOptIn = integrators.filter(i => i.status === 'active' || i.status === 'onboarding')
338
- if (toOptIn.length === 0) return integrators
339
- try {
340
- await acceptInteropTOS()
341
- } catch (err) {
342
- logger.warn({ err }, 'interop: failed to accept TOS')
343
- }
344
- try {
345
- await optInIntegrators(toOptIn.map(i => i.id))
346
- } catch (err) {
347
- logger.warn({ err }, 'interop: failed to opt-in integrators')
348
- }
349
- logger.info({ integrators: toOptIn.map(i => i.name) }, 'interop: initialized')
350
- return integrators
351
- }
298
+ /**
299
+ * Full interop initialization sequence matching WA Web.
300
+ */
301
+ const initInterop = async () => {
302
+ let integrators
303
+ try {
304
+ integrators = await fetchIntegrators()
305
+ } catch (err) {
306
+ logger.warn({ err }, 'interop: failed to fetch integrators')
307
+ return []
308
+ }
309
+ const toOptIn = integrators.filter(i => i.status === 'active' || i.status === 'onboarding')
310
+ if (toOptIn.length === 0) return integrators
311
+ try {
312
+ await acceptInteropTOS()
313
+ } catch (err) {
314
+ logger.warn({ err }, 'interop: failed to accept TOS')
315
+ }
316
+ try {
317
+ await optInIntegrators(toOptIn.map(i => i.id))
318
+ } catch (err) {
319
+ logger.warn({ err }, 'interop: failed to opt-in integrators')
320
+ }
321
+ logger.info({ integrators: toOptIn.map(i => i.name) }, 'interop: initialized')
322
+ return integrators
323
+ }
352
324
 
353
- /**
354
- * Reset the Signal session with an interop contact and force a fresh pkmsg handshake.
355
- * Call this when incoming messages from an interop contact are not arriving or
356
- * failing to decrypt — it clears the stale session so the next sendMessage
357
- * generates a new pre-key message and re-establishes both directions.
358
- */
359
- const resetInteropSession = async jid => {
360
- await signalRepository.deleteSession([jid])
361
- logger.info({ jid }, '[interop] session reset — next send will use pkmsg')
362
- }
325
+ /**
326
+ * Reset the Signal session with an interop contact and force a fresh pkmsg handshake.
327
+ */
328
+ const resetInteropSession = async jid => {
329
+ await signalRepository.deleteSession([jid])
330
+ logger.info({ jid }, '[interop] session reset — next send will use pkmsg')
331
+ }
363
332
 
364
- /**
365
- * Create an interop group via MEX.
366
- * Confirmed from C80913Xf.java opcode 15:
367
- * input: { participants: [{ jid }] }
368
- * dataPath: xwa2_interop_group_create
369
- *
370
- * @param {string[]} participants - Interop JIDs to add
371
- * @returns {{ gid, creationTime, creator, participants }}
372
- */
373
- const createInteropGroup = async participants => {
374
- const result = await mexQuery(
375
- { input: { participants: participants.map(jid => ({ jid })) } },
376
- INTEROP_MEX_QUERY_IDS.CREATE_GROUP,
377
- 'xwa2_interop_group_create'
378
- )
379
- logger.info({ participants }, '[interop] group created via MEX')
380
- return result
381
- }
333
+ /**
334
+ * Create an interop group via MEX.
335
+ */
336
+ const createInteropGroup = async participants => {
337
+ const result = await mexQuery(
338
+ { input: { participants: participants.map(jid => ({ jid })) } },
339
+ INTEROP_MEX_QUERY_IDS.CREATE_GROUP,
340
+ 'xwa2_interop_group_create'
341
+ )
342
+ logger.info({ participants }, '[interop] group created via MEX')
343
+ return result
344
+ }
382
345
 
383
- /**
384
- * Leave one or more interop groups via MEX.
385
- * Confirmed from C80913Xf.java opcode 28:
386
- * input: { groups_to_leave: [{ gid }] }
387
- * dataPath: xwa2_interop_group_leave
388
- *
389
- * gid is the raw interop group ID (without @g.us suffix).
390
- * @param {string|string[]} jids - Interop group JID(s)
391
- */
392
- const leaveInteropGroup = async jids => {
393
- const ids = Array.isArray(jids) ? jids : [jids]
394
- const result = await mexQuery(
395
- { input: { groups_to_leave: ids.map(jid => ({ gid: jid.split('@')[0] })) } },
396
- INTEROP_MEX_QUERY_IDS.LEAVE_GROUP,
397
- 'xwa2_interop_group_leave'
398
- )
399
- logger.info({ jids: ids }, '[interop] left group(s) via MEX')
400
- return result
401
- }
346
+ /**
347
+ * Leave one or more interop groups via MEX.
348
+ */
349
+ const leaveInteropGroup = async jids => {
350
+ const ids = Array.isArray(jids)? jids : [jids]
351
+ const result = await mexQuery(
352
+ { input: { groups_to_leave: ids.map(jid => ({ gid: jid.split('@')[0] })) } },
353
+ INTEROP_MEX_QUERY_IDS.LEAVE_GROUP,
354
+ 'xwa2_interop_group_leave'
355
+ )
356
+ logger.info({ jids: ids }, '[interop] left group(s) via MEX')
357
+ return result
358
+ }
402
359
 
403
- /**
404
- * Add participants to an existing interop group via MEX.
405
- * Confirmed from C3Wt.java opcode 9:
406
- * input: { gid, participants: [{ jid }] }
407
- * dataPath: xwa2_interop_add_participants_to_group
408
- *
409
- * @param {string} groupJid - Interop group JID
410
- * @param {string[]} participants - Interop JIDs to add
411
- */
412
- const addParticipantsToInteropGroup = async (groupJid, participants) => {
413
- const gid = groupJid.split('@')[0]
414
- return mexQuery(
415
- { input: { gid, participants: participants.map(jid => ({ jid })) } },
416
- INTEROP_MEX_QUERY_IDS.ADD_PARTICIPANTS,
417
- 'xwa2_interop_add_participants_to_group'
418
- )
419
- }
360
+ /**
361
+ * Add participants to an existing interop group via MEX.
362
+ */
363
+ const addParticipantsToInteropGroup = async (groupJid, participants) => {
364
+ const gid = groupJid.split('@')[0]
365
+ return mexQuery(
366
+ { input: { gid, participants: participants.map(jid => ({ jid })) } },
367
+ INTEROP_MEX_QUERY_IDS.ADD_PARTICIPANTS,
368
+ 'xwa2_interop_add_participants_to_group'
369
+ )
370
+ }
420
371
 
421
- /**
422
- * Query info about an interop group via MEX.
423
- * Confirmed from C3Wt.java opcode 10:
424
- * group_input: { gid }
425
- * dataPath: xwa2_interop_group_query_by_id
426
- *
427
- * @param {string} groupJid - Interop group JID
428
- */
429
- const queryInteropGroupInfo = async groupJid => {
430
- const gid = groupJid.split('@')[0]
431
- return mexQuery(
432
- { group_input: { gid } },
433
- INTEROP_MEX_QUERY_IDS.QUERY_GROUP_INFO,
434
- 'xwa2_interop_group_query_by_id'
435
- )
436
- }
372
+ /**
373
+ * Query info about an interop group via MEX.
374
+ */
375
+ const queryInteropGroupInfo = async groupJid => {
376
+ const gid = groupJid.split('@')[0]
377
+ return mexQuery({ group_input: { gid } }, INTEROP_MEX_QUERY_IDS.QUERY_GROUP_INFO, 'xwa2_interop_group_query_by_id')
378
+ }
437
379
 
438
- /**
439
- * Update an interop privacy setting via MEX.
440
- * Confirmed from C24385Ak0.java (InteropPrivacySettingsUpdate):
441
- * feature, setting
442
- * dataPath: xwa2_interop_privacy_setting_update
443
- *
444
- * Known features: "GROUPADD"
445
- * Known settings: "ALL" | "CONTACTS" | "NONE"
446
- *
447
- * @param {string} feature - Privacy feature name
448
- * @param {string} setting - New value
449
- */
450
- const updateInteropPrivacySetting = async (feature, setting) => {
451
- return mexQuery(
452
- { feature, setting },
453
- INTEROP_MEX_QUERY_IDS.PRIVACY_SETTINGS_UPDATE,
454
- 'xwa2_interop_privacy_setting_update'
455
- )
456
- }
380
+ /**
381
+ * Update an interop privacy setting via MEX.
382
+ */
383
+ const updateInteropPrivacySetting = async (feature, setting) => {
384
+ return mexQuery(
385
+ { feature, setting },
386
+ INTEROP_MEX_QUERY_IDS.PRIVACY_SETTINGS_UPDATE,
387
+ 'xwa2_interop_privacy_setting_update'
388
+ )
389
+ }
457
390
 
458
- /**
459
- * Update an interop privacy setting with an explicit contact list via MEX.
460
- * Confirmed from C24388Ak8.java (InteropPrivacySettingWithContactListUpdate):
461
- * feature, setting, contacts[], contact_list_type, dhash
462
- * dataPath: xwa2_interop_privacy_setting_with_contact_list_update
463
- *
464
- * Used when setting is "CONTACTS" and you want to specify exact allowed contacts.
465
- *
466
- * @param {string} feature - Privacy feature (e.g. "GROUPADD")
467
- * @param {string} setting - "CONTACTS"
468
- * @param {string[]} contacts - List of JIDs to allow
469
- * @param {string} contactListType - Contact list type string
470
- * @param {string} [dhash] - Hash of the current contact list (or null/"none")
471
- */
472
- const updateInteropPrivacySettingWithContactList = async (
473
- feature,
474
- setting,
475
- contacts,
476
- contactListType,
477
- dhash = 'none'
478
- ) => {
479
- return mexQuery(
480
- { feature, setting, contacts, contact_list_type: contactListType, dhash },
481
- INTEROP_MEX_QUERY_IDS.PRIVACY_SETTINGS_WITH_CONTACT_LIST,
482
- 'xwa2_interop_privacy_setting_with_contact_list_update'
483
- )
484
- }
391
+ /**
392
+ * Update an interop privacy setting with an explicit contact list via MEX.
393
+ */
394
+ const updateInteropPrivacySettingWithContactList = async (
395
+ feature,
396
+ setting,
397
+ contacts,
398
+ contactListType,
399
+ dhash = 'none'
400
+ ) => {
401
+ return mexQuery(
402
+ { feature, setting, contacts, contact_list_type: contactListType, dhash },
403
+ INTEROP_MEX_QUERY_IDS.PRIVACY_SETTINGS_WITH_CONTACT_LIST,
404
+ 'xwa2_interop_privacy_setting_with_contact_list_update'
405
+ )
406
+ }
485
407
 
486
- /**
487
- * Check whether an interop user allows being added to groups (GROUPADD privacy).
488
- * Returns true if the user can be added, false if blocked by their privacy settings.
489
- *
490
- * Mirrors InteropPrivacySettingsManager.A01() in the APK which queries
491
- * the "GROUPADD" feature for the given account.
492
- */
493
- const getInteropGroupAddPrivacy = async (jid, integratorId) => {
494
- const result = await query({
495
- tag: 'iq',
496
- attrs: { type: 'get', xmlns: 'w:interop', to: S_WHATSAPP_NET },
497
- content: [
498
- {
499
- tag: 'privacy',
500
- attrs: { feature: 'GROUPADD' },
501
- content: [
502
- {
503
- tag: 'user',
504
- attrs: {
505
- jid,
506
- integrator_id: integratorId.toString()
507
- }
508
- }
509
- ]
510
- }
511
- ]
512
- })
513
- const privacyNode = getBinaryNodeChild(result, 'privacy')
514
- const userNode = getBinaryNodeChild(privacyNode, 'user')
515
- // "allowed" means the server confirmed the user can be added; any other value or
516
- // absence of the attribute means the privacy setting blocks the add.
517
- return userNode?.attrs?.result === 'allowed'
518
- }
519
-
520
- return {
521
- ...sock,
522
- fetchIntegrators,
523
- acceptInteropTOS,
524
- optInIntegrators,
525
- optOutIntegrators,
526
- resolveInteropUser,
527
- resolveInteropUsers,
528
- getReachabilitySettings,
529
- setReachabilitySettings,
530
- blockInteropUser,
531
- unblockInteropUser,
532
- reportInteropSpam,
533
- trustInteropContact,
534
- initInterop,
535
- resetInteropSession,
536
- createInteropGroup,
537
- leaveInteropGroup,
538
- addParticipantsToInteropGroup,
539
- queryInteropGroupInfo,
540
- updateInteropPrivacySetting,
541
- updateInteropPrivacySettingWithContactList,
542
- getInteropGroupAddPrivacy,
543
- INTEGRATOR_BIRDYCHAT,
544
- INTEGRATOR_HAIKET,
545
- INTEROP_MEX_QUERY_IDS
546
- }
547
- }
408
+ /**
409
+ * Check whether an interop user allows being added to groups (GROUPADD privacy).
410
+ */
411
+ const getInteropGroupAddPrivacy = async (jid, integratorId) => {
412
+ const result = await query({
413
+ tag: 'iq',
414
+ attrs: { type: 'get', xmlns: 'w:interop', to: S_WHATSAPP_NET },
415
+ content: [
416
+ {
417
+ tag: 'privacy',
418
+ attrs: { feature: 'GROUPADD' },
419
+ content: [
420
+ {
421
+ tag: 'user',
422
+ attrs: {
423
+ jid,
424
+ integrator_id: integratorId.toString()
425
+ }
426
+ }
427
+ ]
428
+ }
429
+ ]
430
+ })
431
+ const privacyNode = getBinaryNodeChild(result, 'privacy')
432
+ const userNode = getBinaryNodeChild(privacyNode, 'user')
433
+ return userNode?.attrs?.result === 'allowed'
434
+ }
548
435
 
549
- exports.makeInteropSocket = makeInteropSocket
436
+ return {
437
+ ...sock,
438
+ fetchIntegrators,
439
+ acceptInteropTOS,
440
+ optInIntegrators,
441
+ optOutIntegrators,
442
+ resolveInteropUser,
443
+ resolveInteropUsers,
444
+ getReachabilitySettings,
445
+ setReachabilitySettings,
446
+ blockInteropUser,
447
+ unblockInteropUser,
448
+ reportInteropSpam,
449
+ trustInteropContact,
450
+ initInterop,
451
+ resetInteropSession,
452
+ createInteropGroup,
453
+ leaveInteropGroup,
454
+ addParticipantsToInteropGroup,
455
+ queryInteropGroupInfo,
456
+ updateInteropPrivacySetting,
457
+ updateInteropPrivacySettingWithContactList,
458
+ getInteropGroupAddPrivacy,
459
+ INTEGRATOR_BIRDYCHAT,
460
+ INTEGRATOR_HAIKET,
461
+ INTEROP_MEX_QUERY_IDS
462
+ }
463
+ }