@zervonex/baileys 3.0.2 → 3.0.4
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.
- package/README.md +38 -8
- package/WAProto/WAProtoCompile.js +1 -0
- package/WAProto/fetcher.js +17 -33
- package/WAProto/index.js +34 -26
- package/WAProto/parser.js +333 -312
- package/lib/Builders/AIRich.js +14 -14
- package/lib/Builders/base.js +3 -3
- package/lib/Builders/builders.js +460 -448
- package/lib/Defaults/index.js +1 -0
- package/lib/Socket/messages-recv.js +1 -1
- package/lib/Socket/messages-send.js +1 -1
- package/lib/Socket/newsletter.js +1 -0
- package/lib/Socket/socket.js +4 -3
- package/lib/Socket/zervonex-handler.js +1 -1
- package/lib/Utils/browser-utils.js +8 -4
- package/lib/Utils/chat-utils.js +1 -1
- package/lib/Utils/messages.js +91 -40
- package/lib/Utils/use-multi-file-auth-state.js +50 -157
- package/lib/Utils/validate-connection.js +19 -28
- package/lib/WABinary/generic-utils.js +43 -19
- package/lib/index.js +1 -1
- package/package.json +56 -91
package/WAProto/parser.js
CHANGED
|
@@ -1,313 +1,334 @@
|
|
|
1
|
-
import { createRequire } from 'module'
|
|
2
|
-
import { writeFileSync, renameSync } from 'fs'
|
|
3
|
-
import { join, dirname } from 'path'
|
|
4
|
-
import { fileURLToPath } from 'url'
|
|
5
|
-
import logger from '../lib/Utils/logger.js'
|
|
6
|
-
|
|
7
|
-
const require = createRequire(import.meta.url)
|
|
8
|
-
const acorn = require('acorn')
|
|
9
|
-
const walk = require('acorn-walk')
|
|
10
|
-
|
|
11
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
12
|
-
const addPrefix = (lines, prefix) => lines.map(l => prefix + l)
|
|
13
|
-
|
|
14
|
-
function extractAllExpressions(node) {
|
|
15
|
-
const out = [node]
|
|
16
|
-
const exp = node.expression
|
|
17
|
-
if (exp) out.push(exp)
|
|
18
|
-
if (node?.expression?.arguments?.length)
|
|
19
|
-
for (const arg of node.expression.arguments)
|
|
20
|
-
if (arg?.body?.body?.length)
|
|
21
|
-
for (const e of arg.body.body) out.push(...extractAllExpressions(e))
|
|
22
|
-
if (node?.body?.body?.length)
|
|
23
|
-
for (const e of node.body.body)
|
|
24
|
-
if (e.expression) out.push(...extractAllExpressions(e.expression))
|
|
25
|
-
if (node.expression?.expressions?.length)
|
|
26
|
-
for (const e of node.expression.expressions) out.push(...extractAllExpressions(e))
|
|
27
|
-
return out
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getNumericEnumValue(node) {
|
|
31
|
-
if (node?.type === 'Literal' && typeof node.value === 'number') return node.value
|
|
32
|
-
if (node?.type === 'UnaryExpression' && (node.operator === '-' || node.operator === '+') &&
|
|
33
|
-
node.argument?.type === 'Literal' && typeof node.argument.value === 'number')
|
|
34
|
-
return node.operator === '-' ? -node.argument.value : node.argument.value
|
|
35
|
-
return undefined
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function parseBundleSources(sources) {
|
|
39
|
-
const nodes = []
|
|
40
|
-
const opts = { ecmaVersion: 'latest', allowHashBang: true }
|
|
41
|
-
for (let i = 0; i < sources.length; i++) {
|
|
42
|
-
const patched = sources[i].replaceAll('LimitSharing$Trigger', 'LimitSharing$TriggerType')
|
|
43
|
-
sources[i] = null
|
|
44
|
-
try { nodes.push(...acorn.parse(patched, { ...opts, sourceType: 'script' }).body) }
|
|
45
|
-
catch {
|
|
46
|
-
try { nodes.push(...acorn.parse(patched, { ...opts, sourceType: 'module' }).body) }
|
|
47
|
-
catch (e) { logger.warn(`[WAProto] Skipping bundle ${i + 1}: ${e.message}`) }
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return nodes
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
1
|
+
import { createRequire } from 'module'
|
|
2
|
+
import { writeFileSync, renameSync } from 'fs'
|
|
3
|
+
import { join, dirname } from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
import logger from '../lib/Utils/logger.js'
|
|
6
|
+
|
|
7
|
+
const require = createRequire(import.meta.url)
|
|
8
|
+
const acorn = require('acorn')
|
|
9
|
+
const walk = require('acorn-walk')
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
const addPrefix = (lines, prefix) => lines.map(l => prefix + l)
|
|
13
|
+
|
|
14
|
+
function extractAllExpressions(node) {
|
|
15
|
+
const out = [node]
|
|
16
|
+
const exp = node.expression
|
|
17
|
+
if (exp) out.push(exp)
|
|
18
|
+
if (node?.expression?.arguments?.length)
|
|
19
|
+
for (const arg of node.expression.arguments)
|
|
20
|
+
if (arg?.body?.body?.length)
|
|
21
|
+
for (const e of arg.body.body) out.push(...extractAllExpressions(e))
|
|
22
|
+
if (node?.body?.body?.length)
|
|
23
|
+
for (const e of node.body.body)
|
|
24
|
+
if (e.expression) out.push(...extractAllExpressions(e.expression))
|
|
25
|
+
if (node.expression?.expressions?.length)
|
|
26
|
+
for (const e of node.expression.expressions) out.push(...extractAllExpressions(e))
|
|
27
|
+
return out
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getNumericEnumValue(node) {
|
|
31
|
+
if (node?.type === 'Literal' && typeof node.value === 'number') return node.value
|
|
32
|
+
if (node?.type === 'UnaryExpression' && (node.operator === '-' || node.operator === '+') &&
|
|
33
|
+
node.argument?.type === 'Literal' && typeof node.argument.value === 'number')
|
|
34
|
+
return node.operator === '-' ? -node.argument.value : node.argument.value
|
|
35
|
+
return undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseBundleSources(sources) {
|
|
39
|
+
const nodes = []
|
|
40
|
+
const opts = { ecmaVersion: 'latest', allowHashBang: true }
|
|
41
|
+
for (let i = 0; i < sources.length; i++) {
|
|
42
|
+
const patched = sources[i].replaceAll('LimitSharing$Trigger', 'LimitSharing$TriggerType')
|
|
43
|
+
sources[i] = null
|
|
44
|
+
try { nodes.push(...acorn.parse(patched, { ...opts, sourceType: 'script' }).body) }
|
|
45
|
+
catch {
|
|
46
|
+
try { nodes.push(...acorn.parse(patched, { ...opts, sourceType: 'module' }).body) }
|
|
47
|
+
catch (e) { logger.warn(`[WAProto] Skipping bundle ${i + 1}: ${e.message}`) }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return nodes
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function extractProtoSources(bundle) {
|
|
54
|
+
const out = [], targets = new Set(['"$InternalEnum"', '"WAProtoConst"'])
|
|
55
|
+
let i = 0
|
|
56
|
+
while (i < bundle.length) {
|
|
57
|
+
const start = bundle.indexOf('__d(', i)
|
|
58
|
+
if (start === -1) break
|
|
59
|
+
const ns = bundle.indexOf('"', start + 4)
|
|
60
|
+
const name = bundle.slice(ns, bundle.indexOf('"', ns + 1) + 1)
|
|
61
|
+
if (targets.has(name) || name.endsWith('.pb"')) {
|
|
62
|
+
let depth = 0, j = start
|
|
63
|
+
while (j < bundle.length) {
|
|
64
|
+
if (bundle[j] === '(') depth++
|
|
65
|
+
else if (bundle[j] === ')' && --depth === 0) { out.push(bundle.slice(start, j + 1)); break }
|
|
66
|
+
j++
|
|
67
|
+
}
|
|
68
|
+
i = j + 1
|
|
69
|
+
} else i = start + 4
|
|
70
|
+
}
|
|
71
|
+
return out.length ? out : [bundle]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function filterProtoModules(allNodes) {
|
|
75
|
+
return allNodes.filter(m => extractAllExpressions(m).find(e => e?.left?.property?.name === 'internalSpec'))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function extractSchema(bundleSources) {
|
|
79
|
+
const allNodes = parseBundleSources(extractProtoSources(Array.isArray(bundleSources) ? bundleSources.join('') : bundleSources))
|
|
80
|
+
const modules = filterProtoModules(allNodes)
|
|
81
|
+
|
|
82
|
+
const unspecName = n => n.endsWith('Spec') ? n.slice(0, -4) : n
|
|
83
|
+
const getNesting = n => n.split('$').slice(0, -1).join('$')
|
|
84
|
+
const rename = n => unspecName(n)
|
|
85
|
+
|
|
86
|
+
const modulesInfo = {}
|
|
87
|
+
const moduleIndentationMap = {}
|
|
88
|
+
|
|
89
|
+
modules.forEach(module => {
|
|
90
|
+
const modName = module?.expression?.arguments?.[0]?.value ?? `__anon_${Math.random()}`
|
|
91
|
+
modulesInfo[modName] = { crossRefs: [] }
|
|
92
|
+
walk.simple(module, {
|
|
93
|
+
AssignmentExpression(node) {
|
|
94
|
+
if (node?.right?.type === 'CallExpression' &&
|
|
95
|
+
node?.right?.arguments?.length === 1 &&
|
|
96
|
+
node?.right?.arguments[0]?.type !== 'ObjectExpression')
|
|
97
|
+
modulesInfo[modName].crossRefs.push({ alias: node.left?.name, module: node.right.arguments[0].value })
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
for (const mod of modules) {
|
|
103
|
+
const modName = mod?.expression?.arguments?.[0]?.value
|
|
104
|
+
const modInfo = modulesInfo[modName]
|
|
105
|
+
const assignments = []
|
|
106
|
+
|
|
107
|
+
walk.simple(mod, {
|
|
108
|
+
AssignmentExpression(node) {
|
|
109
|
+
const left = node.left
|
|
110
|
+
if (left?.property?.name &&
|
|
111
|
+
left.property.name !== 'internalSpec' &&
|
|
112
|
+
left.property.name !== 'internalDefaults' &&
|
|
113
|
+
left.property.name !== 'name') assignments.push(left)
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const makeBlankIdent = a => {
|
|
118
|
+
const key = rename(a?.property?.name)
|
|
119
|
+
const indentation = getNesting(key)
|
|
120
|
+
moduleIndentationMap[key] = moduleIndentationMap[key] || {}
|
|
121
|
+
moduleIndentationMap[key].indentation = indentation
|
|
122
|
+
if (indentation.length) {
|
|
123
|
+
moduleIndentationMap[indentation] = moduleIndentationMap[indentation] || {}
|
|
124
|
+
moduleIndentationMap[indentation].members = moduleIndentationMap[indentation].members || new Set()
|
|
125
|
+
moduleIndentationMap[indentation].members.add(key)
|
|
126
|
+
}
|
|
127
|
+
return [key, { name: key }]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
modInfo.identifiers = Object.fromEntries(assignments.map(makeBlankIdent).reverse())
|
|
131
|
+
|
|
132
|
+
const enumAliases = {}
|
|
133
|
+
walk.ancestor(mod, {
|
|
134
|
+
Property(node, anc) {
|
|
135
|
+
const fatherNode = anc[anc.length - 3]
|
|
136
|
+
const fatherFather = anc[anc.length - 4]
|
|
137
|
+
if (fatherNode?.type === 'AssignmentExpression' &&
|
|
138
|
+
fatherNode?.left?.property?.name === 'internalSpec' &&
|
|
139
|
+
fatherNode?.right?.properties?.length) {
|
|
140
|
+
const values = fatherNode.right.properties.map(p => ({ name: p.key.name, id: p.value.value }))
|
|
141
|
+
enumAliases[fatherNode?.left?.name] = values
|
|
142
|
+
} else if (fatherNode?.type === 'VariableDeclarator' &&
|
|
143
|
+
fatherNode?.init?.type === 'ObjectExpression' &&
|
|
144
|
+
fatherNode.init.properties.length &&
|
|
145
|
+
fatherNode.init.properties.every(p => getNumericEnumValue(p.value) !== undefined)) {
|
|
146
|
+
const values = fatherNode.init.properties.map(p => ({ name: p.key.name || p.key.value, id: getNumericEnumValue(p.value) }))
|
|
147
|
+
enumAliases[fatherNode.id?.name] = values
|
|
148
|
+
} else if (node?.key?.name && fatherNode?.arguments?.length > 0) {
|
|
149
|
+
const values = fatherNode.arguments?.[0]?.properties?.map(p => ({ name: p.key.name, id: p.value.value }))
|
|
150
|
+
const nameAlias = fatherFather?.left?.name || fatherFather?.id?.name
|
|
151
|
+
if (nameAlias) enumAliases[nameAlias] = values
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
walk.simple(mod, {
|
|
157
|
+
AssignmentExpression(node) {
|
|
158
|
+
if (node.left?.type === 'MemberExpression' && modInfo.identifiers?.[rename(node.left.property?.name)]) {
|
|
159
|
+
const ident = modInfo.identifiers[rename(node.left.property.name)]
|
|
160
|
+
ident.alias = node.right?.name
|
|
161
|
+
ident.enumValues = enumAliases[ident.alias]
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const findByAlias = (identifiers, alias) => Object.values(identifiers).find(item => item.alias === alias)
|
|
168
|
+
|
|
169
|
+
for (const mod of modules) {
|
|
170
|
+
const modName = mod?.expression?.arguments?.[0]?.value
|
|
171
|
+
const modInfo = modulesInfo[modName]
|
|
172
|
+
|
|
173
|
+
walk.simple(mod, {
|
|
174
|
+
AssignmentExpression(node) {
|
|
175
|
+
if (node.left?.type !== 'MemberExpression' ||
|
|
176
|
+
node.left.property?.name !== 'internalSpec' ||
|
|
177
|
+
node.right?.type !== 'ObjectExpression') return
|
|
178
|
+
|
|
179
|
+
const targetIdent = Object.values(modInfo.identifiers).find(v => v.alias === node.left.object?.name)
|
|
180
|
+
if (!targetIdent) return
|
|
181
|
+
|
|
182
|
+
const constraints = [], rawMembers = []
|
|
183
|
+
for (const p of node.right.properties) {
|
|
184
|
+
p.key.name = p.key.type === 'Identifier' ? p.key.name : p.key.value
|
|
185
|
+
; (p.key.name.substring(0, 2) === '__' ? constraints : rawMembers).push(p)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let members = rawMembers.map(({ key: { name }, value: { elements } }) => {
|
|
189
|
+
let type
|
|
190
|
+
const flags = []
|
|
191
|
+
const unwrapBinaryOr = n =>
|
|
192
|
+
n.type === 'BinaryExpression' && n.operator === '|'
|
|
193
|
+
? [].concat(unwrapBinaryOr(n.left), unwrapBinaryOr(n.right))
|
|
194
|
+
: [n]
|
|
195
|
+
|
|
196
|
+
unwrapBinaryOr(elements[1]).forEach(m => {
|
|
197
|
+
if (m.type !== 'MemberExpression' || m.object?.type !== 'MemberExpression') return
|
|
198
|
+
if (m.object.property?.name === 'TYPES') {
|
|
199
|
+
type = m.property.name.toLowerCase()
|
|
200
|
+
if (type === 'map' && elements[2]?.type === 'ArrayExpression') {
|
|
201
|
+
let typeStr = 'map<'
|
|
202
|
+
elements[2].elements.forEach((el, i) => {
|
|
203
|
+
typeStr += el?.property?.name ? el.property.name.toLowerCase()
|
|
204
|
+
: (findByAlias(modInfo.identifiers, el.name)?.name ?? el.name)
|
|
205
|
+
if (i < elements[2].elements.length - 1) typeStr += ', '
|
|
206
|
+
})
|
|
207
|
+
type = typeStr + '>'
|
|
208
|
+
}
|
|
209
|
+
} else if (m.object.property?.name === 'FLAGS') {
|
|
210
|
+
flags.push(m.property.name.toLowerCase())
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
if (type === 'message' || type === 'enum') {
|
|
215
|
+
if (elements[2]?.type === 'Identifier') {
|
|
216
|
+
const found = Object.values(modInfo.identifiers).find(v => v.alias === elements[2].name)
|
|
217
|
+
type = found?.name
|
|
218
|
+
} else if (elements[2]?.type === 'MemberExpression') {
|
|
219
|
+
const targetAlias = elements[2]?.object?.name || elements[2]?.object?.left?.name || elements[2]?.object?.callee?.name
|
|
220
|
+
const crossRef = modInfo.crossRefs.find(r => r.alias === targetAlias)
|
|
221
|
+
if (elements[1]?.property?.name === 'ENUM' && elements[2]?.property?.name?.includes('Type'))
|
|
222
|
+
type = rename(elements[2].property.name)
|
|
223
|
+
else if (elements[2]?.property?.name?.includes('Spec'))
|
|
224
|
+
type = rename(elements[2].property.name)
|
|
225
|
+
else if (crossRef && crossRef.module !== '$InternalEnum' && modulesInfo[crossRef.module]?.identifiers?.[rename(elements[2].property.name)])
|
|
226
|
+
type = rename(elements[2].property.name)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return { name, id: elements[0].value, type, flags }
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
constraints.forEach(c => {
|
|
234
|
+
if (c.key.name === '__oneofs__' && c.value.type === 'ObjectExpression') {
|
|
235
|
+
const oneofs = c.value.properties.map(p => ({
|
|
236
|
+
name: p.key.name, type: '__oneof__',
|
|
237
|
+
members: p.value.elements.map(e => {
|
|
238
|
+
const idx = members.findIndex(m => m.name === e.value)
|
|
239
|
+
const member = members[idx]
|
|
240
|
+
members.splice(idx, 1)
|
|
241
|
+
return member
|
|
242
|
+
}),
|
|
243
|
+
}))
|
|
244
|
+
members.push(...oneofs)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
targetIdent.members = members
|
|
249
|
+
},
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return { modulesInfo, moduleIndentationMap }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function generateProto3(modulesInfo, moduleIndentationMap, version) {
|
|
257
|
+
const indent = ' '
|
|
258
|
+
const unnest = n => n.split('$').slice(-1)[0]
|
|
259
|
+
|
|
260
|
+
const stringifyMember = (info, completeFlags, parentName) => {
|
|
261
|
+
if (info.type === '__oneof__')
|
|
262
|
+
return [`oneof ${info.name} {`, ...addPrefix([].concat(...info.members.map(m => stringifyMember(m, false))), indent), '}']
|
|
263
|
+
if (info.flags.includes('packed')) { info.flags.splice(info.flags.indexOf('packed'), 1); info.packed = ' [packed=true]' }
|
|
264
|
+
const reqIdx = info.flags.indexOf('required'); if (reqIdx !== -1) info.flags[reqIdx] = 'optional'
|
|
265
|
+
if (completeFlags && !info.flags.length && info.type && !info.type.includes('map')) info.flags.push('optional')
|
|
266
|
+
const indentation = moduleIndentationMap[info.type]?.indentation
|
|
267
|
+
let typeName = unnest(info.type || 'bytes')
|
|
268
|
+
if (indentation !== parentName && indentation) typeName = `${indentation.replaceAll('$', '.')}.${typeName}`
|
|
269
|
+
return [`${info.flags.join(' ')}${info.flags.length ? ' ' : ''}${typeName} ${info.name} = ${info.id}${info.packed || ''};`]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const decodedProtoMap = {}
|
|
273
|
+
|
|
274
|
+
for (const [, modInfo] of Object.entries(modulesInfo)) {
|
|
275
|
+
if (!modInfo.identifiers) continue
|
|
276
|
+
|
|
277
|
+
const stringifyEnum = (ident, overrideName = null) => [
|
|
278
|
+
`enum ${overrideName || ident.displayName || ident.name} {`,
|
|
279
|
+
...addPrefix(ident.enumValues.map(v => `${v.name} = ${v.id};`), indent),
|
|
280
|
+
'}',
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
function stringifyMessage(ident) {
|
|
284
|
+
const nestedKeys = moduleIndentationMap[ident.name]?.members
|
|
285
|
+
const result = [
|
|
286
|
+
`message ${ident.displayName || ident.name} {`,
|
|
287
|
+
...addPrefix([].concat(...ident.members.map(m => stringifyMember(m, true, ident.name))), indent),
|
|
288
|
+
]
|
|
289
|
+
if (nestedKeys?.size) {
|
|
290
|
+
for (const memberName of Array.from(nestedKeys).sort()) {
|
|
291
|
+
let entity = modInfo.identifiers[memberName]
|
|
292
|
+
if (entity) {
|
|
293
|
+
entity = { ...entity, displayName: entity.name.slice(ident.name.length + 1) }
|
|
294
|
+
result.push(...addPrefix(getEntity(entity), indent))
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
result.push('}', '')
|
|
299
|
+
return result
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const getEntity = v => {
|
|
303
|
+
if (v.members) return stringifyMessage(v)
|
|
304
|
+
if (v.enumValues?.length) return stringifyEnum(v)
|
|
305
|
+
return [`// Unknown entity ${v.name}`]
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
for (const v of Object.values(modInfo.identifiers))
|
|
309
|
+
if (!moduleIndentationMap[v.name]?.indentation?.length)
|
|
310
|
+
decodedProtoMap[v.name] = getEntity(v).join('\n')
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const body = Object.keys(decodedProtoMap).sort().map(k => decodedProtoMap[k]).join('\n')
|
|
314
|
+
return `syntax = "proto3";\npackage proto;\n${version ? `\n/// WhatsApp Version: ${version}\n` : ''}\n${body}`
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function parseBundle(bundle, version) {
|
|
318
|
+
const sources = Array.isArray(bundle) ? bundle : [bundle]
|
|
319
|
+
const { modulesInfo, moduleIndentationMap } = extractSchema(sources)
|
|
320
|
+
return generateProto3(modulesInfo, moduleIndentationMap, version)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export async function parseAndWriteProto(bundle, version, outPath = PROTO_FILE) {
|
|
324
|
+
const protoText = parseBundle(bundle, version)
|
|
325
|
+
const tmp = outPath + '.tmp'
|
|
326
|
+
writeFileSync(tmp, protoText, 'utf8')
|
|
327
|
+
renameSync(tmp, outPath)
|
|
328
|
+
const unknown = [...protoText.matchAll(/\/\/ Unknown entity (.+)/g)].map(m => m[1])
|
|
329
|
+
if (unknown.length) logger.warn(`[WAProto] ${unknown.length} unknown entities: ${unknown.join(', ')}`)
|
|
330
|
+
return {
|
|
331
|
+
messageCount: Object.values(modulesInfo).reduce((s, m) => s + Object.keys(m.identifiers || {}).length, 0),
|
|
332
|
+
unknownCount: unknown.length,
|
|
333
|
+
}
|
|
313
334
|
}
|