@wppconnect/wa-proto 0.0.5
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/CHANGELOG.md +35 -0
- package/LICENSE +190 -0
- package/README.md +34 -0
- package/WAProto.proto +3808 -0
- package/dist/index.d.ts +44342 -0
- package/dist/index.js +137957 -0
- package/index-2x.js +488 -0
- package/index.js +526 -0
- package/package.json +56 -0
package/index-2x.js
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright 2024 WPPConnect Team
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Based on https://github.com/WhiskeySockets/Baileys/tree/master/WAProto
|
|
19
|
+
*/
|
|
20
|
+
const request = require('request-promise-native');
|
|
21
|
+
const acorn = require('acorn');
|
|
22
|
+
const walk = require('acorn-walk');
|
|
23
|
+
const fs = require('fs/promises');
|
|
24
|
+
|
|
25
|
+
let whatsAppVersion = 'latest';
|
|
26
|
+
|
|
27
|
+
const addPrefix = (lines, prefix) => lines.map((line) => prefix + line);
|
|
28
|
+
|
|
29
|
+
const extractAllExpressions = (node) => {
|
|
30
|
+
const expressions = [node];
|
|
31
|
+
const exp = node.expression;
|
|
32
|
+
if (exp) {
|
|
33
|
+
expressions.push(exp);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (node.expression?.expressions?.length) {
|
|
37
|
+
for (const exp of node.expression?.expressions) {
|
|
38
|
+
expressions.push(...extractAllExpressions(exp));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return expressions;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
async function findAppModules() {
|
|
46
|
+
const ua = {
|
|
47
|
+
headers: {
|
|
48
|
+
'User-Agent':
|
|
49
|
+
'Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0',
|
|
50
|
+
'Sec-Fetch-Dest': 'script',
|
|
51
|
+
'Sec-Fetch-Mode': 'no-cors',
|
|
52
|
+
'Sec-Fetch-Site': 'same-origin',
|
|
53
|
+
Referer: 'https://web.whatsapp.com/',
|
|
54
|
+
Accept: '*/*',
|
|
55
|
+
'Accept-Language': 'Accept-Language: en-US,en;q=0.5',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const baseURL = 'https://web.whatsapp.com';
|
|
59
|
+
const serviceworker = await request.get(`${baseURL}/serviceworker.js`, ua);
|
|
60
|
+
|
|
61
|
+
const versions = [
|
|
62
|
+
...serviceworker.matchAll(/assets-manifest-([\d\.]+).json/g),
|
|
63
|
+
].map((r) => r[1]);
|
|
64
|
+
const version = versions[0];
|
|
65
|
+
|
|
66
|
+
let bootstrapQRURL = '';
|
|
67
|
+
if (version) {
|
|
68
|
+
const asset = await request.get(
|
|
69
|
+
`${baseURL}/assets-manifest-${version}.json`,
|
|
70
|
+
ua
|
|
71
|
+
);
|
|
72
|
+
const hashFiles = JSON.parse(asset);
|
|
73
|
+
const files = Object.keys(hashFiles);
|
|
74
|
+
const app = files.find((f) => /^app\./.test(f));
|
|
75
|
+
bootstrapQRURL = `${baseURL}/${app}`;
|
|
76
|
+
} else {
|
|
77
|
+
const index = await request.get(baseURL, ua);
|
|
78
|
+
const bootstrapQRID = index.match(/src="\/app.([0-9a-z]{10,}).js"/)[1];
|
|
79
|
+
bootstrapQRURL = baseURL + '/app.' + bootstrapQRID + '.js';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.error('Found source JS URL:', bootstrapQRURL);
|
|
83
|
+
|
|
84
|
+
const qrData = await request.get(bootstrapQRURL, ua);
|
|
85
|
+
const waVersion = qrData.match(
|
|
86
|
+
/(?:appVersion:|VERSION_STR=)"(\d\.\d+\.\d+)"/
|
|
87
|
+
)[1];
|
|
88
|
+
console.log('Current version:', waVersion);
|
|
89
|
+
whatsAppVersion = waVersion;
|
|
90
|
+
// This one list of types is so long that it's split into two JavaScript declarations.
|
|
91
|
+
// The module finder below can't handle it, so just patch it manually here.
|
|
92
|
+
const patchedQrData = qrData.replace(
|
|
93
|
+
't.ActionLinkSpec=void 0,t.TemplateButtonSpec',
|
|
94
|
+
't.ActionLinkSpec=t.TemplateButtonSpec'
|
|
95
|
+
);
|
|
96
|
+
//const patchedQrData = qrData.replace("Spec=void 0,t.", "Spec=t.")
|
|
97
|
+
const qrModules =
|
|
98
|
+
acorn.parse(patchedQrData).body[0].expression.arguments[0].elements[1]
|
|
99
|
+
.properties;
|
|
100
|
+
|
|
101
|
+
const result = qrModules.filter((m) => {
|
|
102
|
+
const hasProto = !!m.value.body.body.find((b) => {
|
|
103
|
+
const expressions = extractAllExpressions(b);
|
|
104
|
+
return expressions?.find(
|
|
105
|
+
(e) => e?.left?.property?.name === 'internalSpec'
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
if (hasProto) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
(async () => {
|
|
117
|
+
const unspecName = (name) =>
|
|
118
|
+
name.endsWith('Spec') ? name.slice(0, -4) : name;
|
|
119
|
+
const unnestName = (name) => name.split('$').slice(-1)[0];
|
|
120
|
+
const getNesting = (name) => name.split('$').slice(0, -1).join('$');
|
|
121
|
+
const makeRenameFunc = () => (name) => {
|
|
122
|
+
name = unspecName(name);
|
|
123
|
+
return name; // .replaceAll('$', '__')
|
|
124
|
+
// return renames[name] ?? unnestName(name)
|
|
125
|
+
};
|
|
126
|
+
// The constructor IDs that can be used for enum types
|
|
127
|
+
// const enumConstructorIDs = [76672, 54302]
|
|
128
|
+
|
|
129
|
+
const modules = await findAppModules();
|
|
130
|
+
|
|
131
|
+
// Sort modules so that whatsapp module id changes don't change the order in the output protobuf schema
|
|
132
|
+
// const modules = []
|
|
133
|
+
// for (const mod of wantedModules) {
|
|
134
|
+
// modules.push(unsortedModules.find(node => node.key.value === mod))
|
|
135
|
+
// }
|
|
136
|
+
|
|
137
|
+
// find aliases of cross references between the wanted modules
|
|
138
|
+
const modulesInfo = {};
|
|
139
|
+
const moduleIndentationMap = {};
|
|
140
|
+
modules.forEach(({ key, value }) => {
|
|
141
|
+
const requiringParam = value.params[2].name;
|
|
142
|
+
modulesInfo[key.value] = { crossRefs: [] };
|
|
143
|
+
walk.simple(value, {
|
|
144
|
+
VariableDeclarator(node) {
|
|
145
|
+
if (
|
|
146
|
+
node.init &&
|
|
147
|
+
node.init.type === 'CallExpression' &&
|
|
148
|
+
node.init.callee.name === requiringParam &&
|
|
149
|
+
node.init.arguments.length === 1
|
|
150
|
+
) {
|
|
151
|
+
modulesInfo[key.value].crossRefs.push({
|
|
152
|
+
alias: node.id.name,
|
|
153
|
+
module: node.init.arguments[0].value,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// find all identifiers and, for enums, their array of values
|
|
161
|
+
for (const mod of modules) {
|
|
162
|
+
const modInfo = modulesInfo[mod.key.value];
|
|
163
|
+
const rename = makeRenameFunc(mod.key.value);
|
|
164
|
+
|
|
165
|
+
// all identifiers will be initialized to "void 0" (i.e. "undefined") at the start, so capture them here
|
|
166
|
+
walk.ancestor(mod, {
|
|
167
|
+
UnaryExpression(node, anc) {
|
|
168
|
+
if (!modInfo.identifiers && node.operator === 'void') {
|
|
169
|
+
const assignments = [];
|
|
170
|
+
let i = 1;
|
|
171
|
+
anc.reverse();
|
|
172
|
+
while (anc[i].type === 'AssignmentExpression') {
|
|
173
|
+
assignments.push(anc[i++].left);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const makeBlankIdent = (a) => {
|
|
177
|
+
const key = rename(a.property.name);
|
|
178
|
+
const indentation = getNesting(key);
|
|
179
|
+
const value = { name: key };
|
|
180
|
+
|
|
181
|
+
moduleIndentationMap[key] = moduleIndentationMap[key] || {};
|
|
182
|
+
moduleIndentationMap[key].indentation = indentation;
|
|
183
|
+
|
|
184
|
+
if (indentation.length) {
|
|
185
|
+
moduleIndentationMap[indentation] =
|
|
186
|
+
moduleIndentationMap[indentation] || {};
|
|
187
|
+
moduleIndentationMap[indentation].members =
|
|
188
|
+
moduleIndentationMap[indentation].members || new Set();
|
|
189
|
+
moduleIndentationMap[indentation].members.add(key);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return [key, value];
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
modInfo.identifiers = Object.fromEntries(
|
|
196
|
+
assignments.map(makeBlankIdent).reverse()
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const enumAliases = {};
|
|
202
|
+
// enums are defined directly, and both enums and messages get a one-letter alias
|
|
203
|
+
walk.simple(mod, {
|
|
204
|
+
VariableDeclarator(node) {
|
|
205
|
+
if (
|
|
206
|
+
node.init?.type === 'CallExpression' &&
|
|
207
|
+
// && enumConstructorIDs.includes(node.init.callee?.arguments?.[0]?.value)
|
|
208
|
+
!!node.init.arguments.length &&
|
|
209
|
+
node.init.arguments[0].type === 'ObjectExpression' &&
|
|
210
|
+
node.init.arguments[0].properties.length
|
|
211
|
+
) {
|
|
212
|
+
const values = node.init.arguments[0].properties.map((p) => ({
|
|
213
|
+
name: p.key.name,
|
|
214
|
+
id: p.value.value,
|
|
215
|
+
}));
|
|
216
|
+
enumAliases[node.id.name] = values;
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
AssignmentExpression(node) {
|
|
220
|
+
if (
|
|
221
|
+
node.left.type === 'MemberExpression' &&
|
|
222
|
+
modInfo.identifiers[rename(node.left.property.name)]
|
|
223
|
+
) {
|
|
224
|
+
const ident = modInfo.identifiers[rename(node.left.property.name)];
|
|
225
|
+
ident.alias = node.right.name;
|
|
226
|
+
// enumAliases[ident.alias] = enumAliases[ident.alias] || []
|
|
227
|
+
ident.enumValues = enumAliases[ident.alias];
|
|
228
|
+
console.log(ident.enumValues);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// find the contents for all protobuf messages
|
|
235
|
+
for (const mod of modules) {
|
|
236
|
+
const modInfo = modulesInfo[mod.key.value];
|
|
237
|
+
const rename = makeRenameFunc(mod.key.value);
|
|
238
|
+
|
|
239
|
+
// message specifications are stored in a "internalSpec" attribute of the respective identifier alias
|
|
240
|
+
walk.simple(mod, {
|
|
241
|
+
AssignmentExpression(node) {
|
|
242
|
+
if (
|
|
243
|
+
node.left.type === 'MemberExpression' &&
|
|
244
|
+
node.left.property.name === 'internalSpec' &&
|
|
245
|
+
node.right.type === 'ObjectExpression'
|
|
246
|
+
) {
|
|
247
|
+
const targetIdent = Object.values(modInfo.identifiers).find(
|
|
248
|
+
(v) => v.alias === node.left.object.name
|
|
249
|
+
);
|
|
250
|
+
if (!targetIdent) {
|
|
251
|
+
console.warn(
|
|
252
|
+
`found message specification for unknown identifier alias: ${node.left.object.name}`
|
|
253
|
+
);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// partition spec properties by normal members and constraints (like "__oneofs__") which will be processed afterwards
|
|
258
|
+
const constraints = [];
|
|
259
|
+
let members = [];
|
|
260
|
+
for (const p of node.right.properties) {
|
|
261
|
+
p.key.name = p.key.type === 'Identifier' ? p.key.name : p.key.value;
|
|
262
|
+
const arr =
|
|
263
|
+
p.key.name.substr(0, 2) === '__' ? constraints : members;
|
|
264
|
+
arr.push(p);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
members = members.map(({ key: { name }, value: { elements } }) => {
|
|
268
|
+
let type;
|
|
269
|
+
const flags = [];
|
|
270
|
+
const unwrapBinaryOr = (n) =>
|
|
271
|
+
n.type === 'BinaryExpression' && n.operator === '|'
|
|
272
|
+
? [].concat(unwrapBinaryOr(n.left), unwrapBinaryOr(n.right))
|
|
273
|
+
: [n];
|
|
274
|
+
|
|
275
|
+
// find type and flags
|
|
276
|
+
unwrapBinaryOr(elements[1]).forEach((m) => {
|
|
277
|
+
if (
|
|
278
|
+
m.type === 'MemberExpression' &&
|
|
279
|
+
m.object.type === 'MemberExpression'
|
|
280
|
+
) {
|
|
281
|
+
if (m.object.property.name === 'TYPES') {
|
|
282
|
+
type = m.property.name.toLowerCase();
|
|
283
|
+
} else if (m.object.property.name === 'FLAGS') {
|
|
284
|
+
flags.push(m.property.name.toLowerCase());
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// determine cross reference name from alias if this member has type "message" or "enum"
|
|
290
|
+
if (type === 'message' || type === 'enum') {
|
|
291
|
+
const currLoc = ` from member '${name}' of message '${targetIdent.name}'`;
|
|
292
|
+
if (elements[2].type === 'Identifier') {
|
|
293
|
+
type = Object.values(modInfo.identifiers).find(
|
|
294
|
+
(v) => v.alias === elements[2].name
|
|
295
|
+
)?.name;
|
|
296
|
+
if (!type) {
|
|
297
|
+
console.warn(
|
|
298
|
+
`unable to find reference of alias '${elements[2].name}'` +
|
|
299
|
+
currLoc
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
} else if (elements[2].type === 'MemberExpression') {
|
|
303
|
+
const crossRef = modInfo.crossRefs.find(
|
|
304
|
+
(r) => r.alias === elements[2].object.name
|
|
305
|
+
);
|
|
306
|
+
if (
|
|
307
|
+
crossRef &&
|
|
308
|
+
modulesInfo[crossRef.module].identifiers[
|
|
309
|
+
rename(elements[2].property.name)
|
|
310
|
+
]
|
|
311
|
+
) {
|
|
312
|
+
type = rename(elements[2].property.name);
|
|
313
|
+
} else {
|
|
314
|
+
console.warn(
|
|
315
|
+
`unable to find reference of alias to other module '${elements[2].object.name}' or to message ${elements[2].property.name} of this module` +
|
|
316
|
+
currLoc
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return { name, id: elements[0].value, type, flags };
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// resolve constraints for members
|
|
326
|
+
constraints.forEach((c) => {
|
|
327
|
+
if (
|
|
328
|
+
c.key.name === '__oneofs__' &&
|
|
329
|
+
c.value.type === 'ObjectExpression'
|
|
330
|
+
) {
|
|
331
|
+
const newOneOfs = c.value.properties.map((p) => ({
|
|
332
|
+
name: p.key.name,
|
|
333
|
+
type: '__oneof__',
|
|
334
|
+
members: p.value.elements.map((e) => {
|
|
335
|
+
const idx = members.findIndex((m) => m.name === e.value);
|
|
336
|
+
const member = members[idx];
|
|
337
|
+
members.splice(idx, 1);
|
|
338
|
+
return member;
|
|
339
|
+
}),
|
|
340
|
+
}));
|
|
341
|
+
members.push(...newOneOfs);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
targetIdent.members = members;
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const decodedProtoMap = {};
|
|
352
|
+
const spaceIndent = ' '.repeat(4);
|
|
353
|
+
for (const mod of modules) {
|
|
354
|
+
const modInfo = modulesInfo[mod.key.value];
|
|
355
|
+
const identifiers = Object.values(modInfo.identifiers);
|
|
356
|
+
|
|
357
|
+
// enum stringifying function
|
|
358
|
+
const stringifyEnum = (ident, overrideName = null) =>
|
|
359
|
+
[].concat(
|
|
360
|
+
[`enum ${overrideName || ident.displayName || ident.name} {`],
|
|
361
|
+
addPrefix(
|
|
362
|
+
ident.enumValues.map((v) => `${v.name} = ${v.id};`),
|
|
363
|
+
spaceIndent
|
|
364
|
+
),
|
|
365
|
+
['}']
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// message specification member stringifying function
|
|
369
|
+
const stringifyMessageSpecMember = (
|
|
370
|
+
info,
|
|
371
|
+
completeFlags,
|
|
372
|
+
parentName = undefined
|
|
373
|
+
) => {
|
|
374
|
+
if (info.type === '__oneof__') {
|
|
375
|
+
return [].concat(
|
|
376
|
+
[`oneof ${info.name} {`],
|
|
377
|
+
addPrefix(
|
|
378
|
+
[].concat(
|
|
379
|
+
...info.members.map((m) => stringifyMessageSpecMember(m, false))
|
|
380
|
+
),
|
|
381
|
+
spaceIndent
|
|
382
|
+
),
|
|
383
|
+
['}']
|
|
384
|
+
);
|
|
385
|
+
} else {
|
|
386
|
+
if (info.flags.includes('packed')) {
|
|
387
|
+
info.flags.splice(info.flags.indexOf('packed'));
|
|
388
|
+
info.packed = ' [packed=true]';
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (completeFlags && info.flags.length === 0) {
|
|
392
|
+
info.flags.push('optional');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const ret = [];
|
|
396
|
+
const indentation = moduleIndentationMap[info.type]?.indentation;
|
|
397
|
+
let typeName = unnestName(info.type);
|
|
398
|
+
if (indentation !== parentName && indentation) {
|
|
399
|
+
typeName = `${indentation.replaceAll('$', '.')}.${typeName}`;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// if(info.enumValues) {
|
|
403
|
+
// // typeName = unnestName(info.type)
|
|
404
|
+
// ret = stringifyEnum(info, typeName)
|
|
405
|
+
// }
|
|
406
|
+
|
|
407
|
+
ret.push(
|
|
408
|
+
`${
|
|
409
|
+
info.flags.join(' ') + (info.flags.length === 0 ? '' : ' ')
|
|
410
|
+
}${typeName} ${info.name} = ${info.id}${info.packed || ''};`
|
|
411
|
+
);
|
|
412
|
+
return ret;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// message specification stringifying function
|
|
417
|
+
const stringifyMessageSpec = (ident) => {
|
|
418
|
+
const members = moduleIndentationMap[ident.name]?.members;
|
|
419
|
+
const result = [];
|
|
420
|
+
result.push(
|
|
421
|
+
`message ${ident.displayName || ident.name} {`,
|
|
422
|
+
...addPrefix(
|
|
423
|
+
[].concat(
|
|
424
|
+
...ident.members.map((m) =>
|
|
425
|
+
stringifyMessageSpecMember(m, true, ident.name)
|
|
426
|
+
)
|
|
427
|
+
),
|
|
428
|
+
spaceIndent
|
|
429
|
+
)
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
if (members?.size) {
|
|
433
|
+
const sortedMembers = Array.from(members).sort();
|
|
434
|
+
for (const memberName of sortedMembers) {
|
|
435
|
+
let entity = modInfo.identifiers[memberName];
|
|
436
|
+
if (entity) {
|
|
437
|
+
const displayName = entity.name.slice(ident.name.length + 1);
|
|
438
|
+
entity = { ...entity, displayName };
|
|
439
|
+
result.push(...addPrefix(getEntity(entity), spaceIndent));
|
|
440
|
+
} else {
|
|
441
|
+
console.log('missing nested entity ', memberName);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
result.push('}');
|
|
447
|
+
result.push('');
|
|
448
|
+
|
|
449
|
+
return result;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const getEntity = (v) => {
|
|
453
|
+
let result;
|
|
454
|
+
if (v.members) {
|
|
455
|
+
result = stringifyMessageSpec(v);
|
|
456
|
+
} else if (v.enumValues?.length) {
|
|
457
|
+
result = stringifyEnum(v);
|
|
458
|
+
} else {
|
|
459
|
+
result = ['// Unknown entity ' + v.name];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return result;
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const stringifyEntity = (v) => {
|
|
466
|
+
return {
|
|
467
|
+
content: getEntity(v).join('\n'),
|
|
468
|
+
name: v.name,
|
|
469
|
+
};
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
for (const value of identifiers) {
|
|
473
|
+
const { name, content } = stringifyEntity(value);
|
|
474
|
+
if (!moduleIndentationMap[name]?.indentation?.length) {
|
|
475
|
+
decodedProtoMap[name] = content;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const decodedProto = Object.keys(decodedProtoMap).sort();
|
|
481
|
+
const sortedStr = decodedProto.map((d) => decodedProtoMap[d]).join('\n');
|
|
482
|
+
|
|
483
|
+
const decodedProtoStr = `syntax = "proto2";\npackage waproto;\n\n/// WhatsApp Version: ${whatsAppVersion}\n\n${sortedStr}`;
|
|
484
|
+
const destinationPath = 'WAProto.proto';
|
|
485
|
+
await fs.writeFile(destinationPath, decodedProtoStr);
|
|
486
|
+
|
|
487
|
+
console.log(`Extracted protobuf schema to "${destinationPath}"`);
|
|
488
|
+
})();
|