@yemo-dev/yebail 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 (106) hide show
  1. package/EXAMPLES.md +641 -0
  2. package/LICENSE +21 -0
  3. package/README.md +141 -0
  4. package/WAProto/GenerateStatics.sh +4 -0
  5. package/WAProto/WAProto.proto +4775 -0
  6. package/WAProto/index.js +116311 -0
  7. package/engine-requirements.js +10 -0
  8. package/lib/Defaults/index.js +142 -0
  9. package/lib/Defaults/phonenumber-mcc.json +223 -0
  10. package/lib/Defaults/yebail-version.json +7 -0
  11. package/lib/Signal/Group/ciphertext-message.js +15 -0
  12. package/lib/Signal/Group/group-session-builder.js +64 -0
  13. package/lib/Signal/Group/group_cipher.js +96 -0
  14. package/lib/Signal/Group/index.js +57 -0
  15. package/lib/Signal/Group/keyhelper.js +55 -0
  16. package/lib/Signal/Group/queue-job.js +57 -0
  17. package/lib/Signal/Group/sender-chain-key.js +34 -0
  18. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  19. package/lib/Signal/Group/sender-key-message.js +69 -0
  20. package/lib/Signal/Group/sender-key-name.js +51 -0
  21. package/lib/Signal/Group/sender-key-record.js +53 -0
  22. package/lib/Signal/Group/sender-key-state.js +99 -0
  23. package/lib/Signal/Group/sender-message-key.js +29 -0
  24. package/lib/Signal/libsignal.js +196 -0
  25. package/lib/Signal/lid-mapping.js +148 -0
  26. package/lib/Socket/Client/index.js +18 -0
  27. package/lib/Socket/Client/types.js +13 -0
  28. package/lib/Socket/Client/websocket.js +72 -0
  29. package/lib/Socket/business.js +368 -0
  30. package/lib/Socket/chats.js +992 -0
  31. package/lib/Socket/communities.js +430 -0
  32. package/lib/Socket/groups.js +323 -0
  33. package/lib/Socket/index.js +10 -0
  34. package/lib/Socket/messages-recv.js +1133 -0
  35. package/lib/Socket/messages-send.js +992 -0
  36. package/lib/Socket/newsletter.js +250 -0
  37. package/lib/Socket/socket.js +631 -0
  38. package/lib/Socket/usync.js +70 -0
  39. package/lib/Store/index.js +8 -0
  40. package/lib/Store/make-in-memory-store.js +421 -0
  41. package/lib/Store/make-ordered-dictionary.js +81 -0
  42. package/lib/Store/object-repository.js +27 -0
  43. package/lib/Types/Auth.js +2 -0
  44. package/lib/Types/Call.js +2 -0
  45. package/lib/Types/Chat.js +4 -0
  46. package/lib/Types/Contact.js +2 -0
  47. package/lib/Types/Events.js +2 -0
  48. package/lib/Types/GroupMetadata.js +2 -0
  49. package/lib/Types/Label.js +27 -0
  50. package/lib/Types/LabelAssociation.js +9 -0
  51. package/lib/Types/Message.js +7 -0
  52. package/lib/Types/Newsletter.js +18 -0
  53. package/lib/Types/Product.js +2 -0
  54. package/lib/Types/Signal.js +2 -0
  55. package/lib/Types/Socket.js +2 -0
  56. package/lib/Types/State.js +2 -0
  57. package/lib/Types/USync.js +2 -0
  58. package/lib/Types/index.js +42 -0
  59. package/lib/Utils/auth-utils.js +188 -0
  60. package/lib/Utils/browser-utils.js +35 -0
  61. package/lib/Utils/business.js +230 -0
  62. package/lib/Utils/chat-utils.js +763 -0
  63. package/lib/Utils/crypto.js +187 -0
  64. package/lib/Utils/decode-wa-message.js +293 -0
  65. package/lib/Utils/event-buffer.js +514 -0
  66. package/lib/Utils/generics.js +453 -0
  67. package/lib/Utils/history.js +94 -0
  68. package/lib/Utils/index.js +37 -0
  69. package/lib/Utils/link-preview.js +121 -0
  70. package/lib/Utils/logger.js +7 -0
  71. package/lib/Utils/lt-hash.js +47 -0
  72. package/lib/Utils/make-mutex.js +43 -0
  73. package/lib/Utils/message-retry-manager.js +128 -0
  74. package/lib/Utils/messages-media.js +910 -0
  75. package/lib/Utils/messages.js +1129 -0
  76. package/lib/Utils/noise-handler.js +150 -0
  77. package/lib/Utils/process-message.js +448 -0
  78. package/lib/Utils/signal.js +150 -0
  79. package/lib/Utils/use-custom-auth-state.js +110 -0
  80. package/lib/Utils/use-multi-file-auth-state.js +43 -0
  81. package/lib/Utils/use-sqlite-auth-state.js +39 -0
  82. package/lib/Utils/validate-connection.js +237 -0
  83. package/lib/Utils/yebail-event-stream.js +55 -0
  84. package/lib/WABinary/constants.js +1303 -0
  85. package/lib/WABinary/decode.js +275 -0
  86. package/lib/WABinary/encode.js +250 -0
  87. package/lib/WABinary/generic-utils.js +110 -0
  88. package/lib/WABinary/index.js +21 -0
  89. package/lib/WABinary/jid-utils.js +136 -0
  90. package/lib/WABinary/types.js +2 -0
  91. package/lib/WAM/BinaryInfo.js +13 -0
  92. package/lib/WAM/constants.js +15350 -0
  93. package/lib/WAM/encode.js +155 -0
  94. package/lib/WAM/index.js +19 -0
  95. package/lib/WAUSync/Protocols/USyncContactProtocol.js +32 -0
  96. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  97. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  98. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
  99. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -0
  100. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  101. package/lib/WAUSync/Protocols/index.js +20 -0
  102. package/lib/WAUSync/USyncQuery.js +89 -0
  103. package/lib/WAUSync/USyncUser.js +26 -0
  104. package/lib/WAUSync/index.js +19 -0
  105. package/lib/index.js +46 -0
  106. package/package.json +114 -0
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getNextPreKeysNode = exports.getNextPreKeys = exports.extractDeviceJids = exports.parseAndInjectE2ESessions = exports.xmppPreKey = exports.xmppSignedPreKey = exports.generateOrGetPreKeys = exports.getPreKeys = exports.createSignalIdentity = void 0;
4
+ const lodash_1 = require("lodash");
5
+ const Defaults_1 = require("../Defaults");
6
+ const WABinary_1 = require("../WABinary");
7
+ const crypto_1 = require("./crypto");
8
+ const generics_1 = require("./generics");
9
+ const createSignalIdentity = (wid, accountSignatureKey) => {
10
+ return {
11
+ identifier: { name: wid, deviceId: 0 },
12
+ identifierKey: (0, crypto_1.generateSignalPubKey)(accountSignatureKey)
13
+ };
14
+ };
15
+ exports.createSignalIdentity = createSignalIdentity;
16
+ const getPreKeys = async ({ get }, min, limit) => {
17
+ const idList = [];
18
+ for (let id = min; id < limit; id++) {
19
+ idList.push(id.toString());
20
+ }
21
+ return get('pre-key', idList);
22
+ };
23
+ exports.getPreKeys = getPreKeys;
24
+ const generateOrGetPreKeys = (creds, range) => {
25
+ const avaliable = creds.nextPreKeyId - creds.firstUnuploadedPreKeyId;
26
+ const remaining = range - avaliable;
27
+ const lastPreKeyId = creds.nextPreKeyId + remaining - 1;
28
+ const newPreKeys = {};
29
+ if (remaining > 0) {
30
+ for (let i = creds.nextPreKeyId; i <= lastPreKeyId; i++) {
31
+ newPreKeys[i] = crypto_1.Curve.generateKeyPair();
32
+ }
33
+ }
34
+ return {
35
+ newPreKeys,
36
+ lastPreKeyId,
37
+ preKeysRange: [creds.firstUnuploadedPreKeyId, range],
38
+ };
39
+ };
40
+ exports.generateOrGetPreKeys = generateOrGetPreKeys;
41
+ const xmppSignedPreKey = (key) => ({
42
+ tag: 'skey',
43
+ attrs: {},
44
+ content: [
45
+ { tag: 'id', attrs: {}, content: (0, generics_1.encodeBigEndian)(key.keyId, 3) },
46
+ { tag: 'value', attrs: {}, content: key.keyPair.public },
47
+ { tag: 'signature', attrs: {}, content: key.signature }
48
+ ]
49
+ });
50
+ exports.xmppSignedPreKey = xmppSignedPreKey;
51
+ const xmppPreKey = (pair, id) => ({
52
+ tag: 'key',
53
+ attrs: {},
54
+ content: [
55
+ { tag: 'id', attrs: {}, content: (0, generics_1.encodeBigEndian)(id, 3) },
56
+ { tag: 'value', attrs: {}, content: pair.public }
57
+ ]
58
+ });
59
+ exports.xmppPreKey = xmppPreKey;
60
+ const parseAndInjectE2ESessions = async (node, repository) => {
61
+ const extractKey = (key) => (key ? ({
62
+ keyId: (0, WABinary_1.getBinaryNodeChildUInt)(key, 'id', 3),
63
+ publicKey: (0, crypto_1.generateSignalPubKey)((0, WABinary_1.getBinaryNodeChildBuffer)(key, 'value')),
64
+ signature: (0, WABinary_1.getBinaryNodeChildBuffer)(key, 'signature'),
65
+ }) : undefined);
66
+ const nodes = (0, WABinary_1.getBinaryNodeChildren)((0, WABinary_1.getBinaryNodeChild)(node, 'list'), 'user');
67
+ for (const node of nodes) {
68
+ (0, WABinary_1.assertNodeErrorFree)(node);
69
+ }
70
+ // Most of the work in repository.injectE2ESession is CPU intensive, not IO
71
+ // So Promise.all doesn't really help here,
72
+ // but blocks even loop if we're using it inside keys.transaction, and it makes it "sync" actually
73
+ // This way we chunk it in smaller parts and between those parts we can yield to the event loop
74
+ // It's rare case when you need to E2E sessions for so many users, but it's possible
75
+ const chunkSize = 100;
76
+ const chunks = (0, lodash_1.chunk)(nodes, chunkSize);
77
+ for (const nodesChunk of chunks) {
78
+ await Promise.all(nodesChunk.map(async (node) => {
79
+ const signedKey = (0, WABinary_1.getBinaryNodeChild)(node, 'skey');
80
+ const key = (0, WABinary_1.getBinaryNodeChild)(node, 'key');
81
+ const identity = (0, WABinary_1.getBinaryNodeChildBuffer)(node, 'identity');
82
+ const jid = node.attrs.jid;
83
+ const registrationId = (0, WABinary_1.getBinaryNodeChildUInt)(node, 'registration', 4);
84
+ await repository.injectE2ESession({
85
+ jid,
86
+ session: {
87
+ registrationId: registrationId,
88
+ identityKey: (0, crypto_1.generateSignalPubKey)(identity),
89
+ signedPreKey: extractKey(signedKey),
90
+ preKey: extractKey(key)
91
+ }
92
+ });
93
+ }));
94
+ }
95
+ };
96
+ exports.parseAndInjectE2ESessions = parseAndInjectE2ESessions;
97
+ const extractDeviceJids = (result, myJid, excludeZeroDevices) => {
98
+ const { user: myUser, device: myDevice } = (0, WABinary_1.jidDecode)(myJid);
99
+ const extracted = [];
100
+ for (const userResult of result) {
101
+ const { devices, id } = userResult;
102
+ const { user } = (0, WABinary_1.jidDecode)(id);
103
+ const deviceList = devices === null || devices === void 0 ? void 0 : devices.deviceList;
104
+ if (Array.isArray(deviceList)) {
105
+ for (const { id: device, keyIndex } of deviceList) {
106
+ if ((!excludeZeroDevices || device !== 0) && // if zero devices are not-excluded, or device is non zero
107
+ (myUser !== user || myDevice !== device) && // either different user or if me user, not this device
108
+ (device === 0 || !!keyIndex) // ensure that "key-index" is specified for "non-zero" devices, produces a bad req otherwise
109
+ ) {
110
+ extracted.push({ user, device });
111
+ }
112
+ }
113
+ }
114
+ }
115
+ return extracted;
116
+ };
117
+ exports.extractDeviceJids = extractDeviceJids;
118
+
119
+ const getNextPreKeys = async ({ creds, keys }, count) => {
120
+ const { newPreKeys, lastPreKeyId, preKeysRange } = (0, exports.generateOrGetPreKeys)(creds, count);
121
+ const update = {
122
+ nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId),
123
+ firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1)
124
+ };
125
+ await keys.set({ 'pre-key': newPreKeys });
126
+ const preKeys = await (0, exports.getPreKeys)(keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1]);
127
+ return { update, preKeys };
128
+ };
129
+ exports.getNextPreKeys = getNextPreKeys;
130
+ const getNextPreKeysNode = async (state, count) => {
131
+ const { creds } = state;
132
+ const { update, preKeys } = await (0, exports.getNextPreKeys)(state, count);
133
+ const node = {
134
+ tag: 'iq',
135
+ attrs: {
136
+ xmlns: 'encrypt',
137
+ type: 'set',
138
+ to: WABinary_1.S_WHATSAPP_NET,
139
+ },
140
+ content: [
141
+ { tag: 'registration', attrs: {}, content: (0, generics_1.encodeBigEndian)(creds.registrationId) },
142
+ { tag: 'type', attrs: {}, content: Defaults_1.KEY_BUNDLE_TYPE },
143
+ { tag: 'identity', attrs: {}, content: creds.signedIdentityKey.public },
144
+ { tag: 'list', attrs: {}, content: Object.keys(preKeys).map(k => (0, exports.xmppPreKey)(preKeys[+k], +k)) },
145
+ (0, exports.xmppSignedPreKey)(creds.signedPreKey)
146
+ ]
147
+ };
148
+ return { update, node };
149
+ };
150
+ exports.getNextPreKeysNode = getNextPreKeysNode;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useCustomAuthState = exports.fixAuthStorageKey = void 0;
4
+ const async_mutex_1 = require("async-mutex");
5
+ const WAProto_1 = require("../../WAProto");
6
+ const auth_utils_1 = require("./auth-utils");
7
+ const generics_1 = require("./generics");
8
+ const keyLocks = new Map();
9
+ const getKeyLock = (storageKey) => {
10
+ let mutex = keyLocks.get(storageKey);
11
+ if (!mutex) {
12
+ mutex = new async_mutex_1.Mutex();
13
+ keyLocks.set(storageKey, mutex);
14
+ }
15
+ return mutex;
16
+ };
17
+ /**
18
+ * Normalizes logical filenames (same rules as multi-file auth) for use as KV keys in any backend.
19
+ */
20
+ const fixAuthStorageKey = (file) => {
21
+ var _a;
22
+ return (_a = file === null || file === void 0 ? void 0 : file.replace(/\//g, '__')) === null || _a === void 0 ? void 0 : _a.replace(/:/g, '-');
23
+ };
24
+ exports.fixAuthStorageKey = fixAuthStorageKey;
25
+ /**
26
+ * Pluggable auth persistence: implement get/set/remove for string keys and JSON string values.
27
+ * Use for SQLite, Redis, MongoDB, cloud HTTP APIs, or any database — same shape as `useMultiFileAuthState`.
28
+ */
29
+ const useCustomAuthState = async (adapter) => {
30
+ if (!adapter || typeof adapter.get !== 'function' || typeof adapter.set !== 'function' || typeof adapter.remove !== 'function') {
31
+ throw new Error('useCustomAuthState: adapter must implement get(key), set(key, jsonString), remove(key)');
32
+ }
33
+ const readData = async (file) => {
34
+ const storageKey = fixAuthStorageKey(file);
35
+ const mutex = getKeyLock(storageKey);
36
+ return mutex.acquire().then(async (release) => {
37
+ try {
38
+ const raw = await adapter.get(storageKey);
39
+ if (raw == null || raw === '') {
40
+ return null;
41
+ }
42
+ return JSON.parse(raw, generics_1.BufferJSON.reviver);
43
+ }
44
+ catch (_a) {
45
+ return null;
46
+ }
47
+ finally {
48
+ release();
49
+ }
50
+ });
51
+ };
52
+ const writeData = async (data, file) => {
53
+ const storageKey = fixAuthStorageKey(file);
54
+ const mutex = getKeyLock(storageKey);
55
+ return mutex.acquire().then(async (release) => {
56
+ try {
57
+ await adapter.set(storageKey, JSON.stringify(data, generics_1.BufferJSON.replacer));
58
+ }
59
+ finally {
60
+ release();
61
+ }
62
+ });
63
+ };
64
+ const removeData = async (file) => {
65
+ const storageKey = fixAuthStorageKey(file);
66
+ const mutex = getKeyLock(storageKey);
67
+ return mutex.acquire().then(async (release) => {
68
+ try {
69
+ await adapter.remove(storageKey);
70
+ }
71
+ finally {
72
+ release();
73
+ }
74
+ });
75
+ };
76
+ const creds = (await readData('creds.json')) || (0, auth_utils_1.initAuthCreds)();
77
+ return {
78
+ state: {
79
+ creds,
80
+ keys: {
81
+ get: async (type, ids) => {
82
+ const data = {};
83
+ await Promise.all(ids.map(async (id) => {
84
+ let value = await readData(`${type}-${id}.json`);
85
+ if (type === 'app-state-sync-key' && value) {
86
+ value = WAProto_1.proto.Message.AppStateSyncKeyData.fromObject(value);
87
+ }
88
+ data[id] = value;
89
+ }));
90
+ return data;
91
+ },
92
+ set: async (data) => {
93
+ const tasks = [];
94
+ for (const category in data) {
95
+ for (const id in data[category]) {
96
+ const value = data[category][id];
97
+ const file = `${category}-${id}.json`;
98
+ tasks.push(value ? writeData(value, file) : removeData(file));
99
+ }
100
+ }
101
+ await Promise.all(tasks);
102
+ }
103
+ }
104
+ },
105
+ saveCreds: async () => {
106
+ return writeData(creds, 'creds.json');
107
+ }
108
+ };
109
+ };
110
+ exports.useCustomAuthState = useCustomAuthState;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useMultiFileAuthState = void 0;
4
+ const promises_1 = require("fs/promises");
5
+ const path_1 = require("path");
6
+ const use_custom_auth_state_1 = require("./use-custom-auth-state");
7
+ /**
8
+ * Local filesystem auth (JSON files). Same data model as `useCustomAuthState` / cloud DB adapters.
9
+ */
10
+ const useMultiFileAuthState = async (folder) => {
11
+ const folderInfo = await (0, promises_1.stat)(folder).catch(() => { });
12
+ if (folderInfo) {
13
+ if (!folderInfo.isDirectory()) {
14
+ throw new Error(`found something that is not a directory at ${folder}, either delete it or specify a different location`);
15
+ }
16
+ }
17
+ else {
18
+ await (0, promises_1.mkdir)(folder, { recursive: true });
19
+ }
20
+ return (0, use_custom_auth_state_1.useCustomAuthState)({
21
+ get: async (key) => {
22
+ try {
23
+ const filePath = (0, path_1.join)(folder, key);
24
+ return await (0, promises_1.readFile)(filePath, { encoding: 'utf-8' });
25
+ }
26
+ catch (_a) {
27
+ return null;
28
+ }
29
+ },
30
+ set: async (key, value) => {
31
+ const filePath = (0, path_1.join)(folder, key);
32
+ await (0, promises_1.writeFile)(filePath, value, 'utf-8');
33
+ },
34
+ remove: async (key) => {
35
+ try {
36
+ await (0, promises_1.unlink)((0, path_1.join)(folder, key));
37
+ }
38
+ catch (_a) {
39
+ }
40
+ }
41
+ });
42
+ };
43
+ exports.useMultiFileAuthState = useMultiFileAuthState;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSqliteAuthState = void 0;
4
+ const use_custom_auth_state_1 = require("./use-custom-auth-state");
5
+ /**
6
+ * SQLite-backed auth (single .db file, one row per key). Requires optional dependency `better-sqlite3`:
7
+ * `npm install better-sqlite3`
8
+ */
9
+ const useSqliteAuthState = async (databaseFilePath) => {
10
+ let Database;
11
+ try {
12
+ Database = require('better-sqlite3');
13
+ }
14
+ catch (_a) {
15
+ throw new Error('useSqliteAuthState requires better-sqlite3: npm install better-sqlite3');
16
+ }
17
+ const db = new Database(databaseFilePath);
18
+ db.exec(`
19
+ CREATE TABLE IF NOT EXISTS yebails_auth_kv (
20
+ k TEXT PRIMARY KEY NOT NULL,
21
+ v TEXT NOT NULL
22
+ )`);
23
+ const sel = db.prepare('SELECT v FROM yebails_auth_kv WHERE k = ?');
24
+ const upsert = db.prepare('INSERT OR REPLACE INTO yebails_auth_kv (k, v) VALUES (?, ?)');
25
+ const del = db.prepare('DELETE FROM yebails_auth_kv WHERE k = ?');
26
+ return (0, use_custom_auth_state_1.useCustomAuthState)({
27
+ get: async (key) => {
28
+ const row = sel.get(key);
29
+ return row ? row.v : null;
30
+ },
31
+ set: async (key, value) => {
32
+ upsert.run(key, value);
33
+ },
34
+ remove: async (key) => {
35
+ del.run(key);
36
+ }
37
+ });
38
+ };
39
+ exports.useSqliteAuthState = useSqliteAuthState;
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodeSignedDeviceIdentity = exports.configureSuccessfulPairing = exports.generateRegistrationNode = exports.generateLoginNode = void 0;
4
+ const boom_1 = require("@hapi/boom");
5
+ const crypto_1 = require("crypto");
6
+ const WAProto_1 = require("../../WAProto");
7
+ const Defaults_1 = require("../Defaults");
8
+ const WABinary_1 = require("../WABinary");
9
+ const crypto_2 = require("./crypto");
10
+ const generics_1 = require("./generics");
11
+ const signal_1 = require("./signal");
12
+
13
+ const getUserAgent = (config) => {
14
+ let mccDigits = '000';
15
+ if (config.mcc != null && String(config.mcc).trim() !== '') {
16
+ const digits = String(config.mcc).replace(/\D/g, '');
17
+ mccDigits = digits.length ? digits.padStart(3, '0').slice(-3) : (0, Defaults_1.getMccForCountryIso2)(config.countryCode || 'US');
18
+ }
19
+ else {
20
+ mccDigits = (0, Defaults_1.getMccForCountryIso2)(config.countryCode || 'US');
21
+ }
22
+ return {
23
+ appVersion: {
24
+ primary: config.version[0],
25
+ secondary: config.version[1],
26
+ tertiary: config.version[2],
27
+ },
28
+ platform: WAProto_1.proto.ClientPayload.UserAgent.Platform.WEB,
29
+ releaseChannel: WAProto_1.proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
30
+ osVersion: '0.1',
31
+ device: 'Desktop',
32
+ osBuildNumber: '0.1',
33
+ localeLanguageIso6391: 'en',
34
+ mnc: '000',
35
+ mcc: mccDigits,
36
+ localeCountryIso31661Alpha2: config.countryCode || 'US'
37
+ };
38
+ };
39
+
40
+ const PLATFORM_MAP = {
41
+ 'Mac OS': WAProto_1.proto.ClientPayload.WebInfo.WebSubPlatform.DARWIN,
42
+ 'Windows': WAProto_1.proto.ClientPayload.WebInfo.WebSubPlatform.WIN32
43
+ };
44
+
45
+ const getWebInfo = (config) => {
46
+ let webSubPlatform = WAProto_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER;
47
+ if (config.syncFullHistory && PLATFORM_MAP[config.browser[0]] && config.browser[1] === 'Desktop') {
48
+ webSubPlatform = PLATFORM_MAP[config.browser[0]];
49
+ }
50
+ return { webSubPlatform };
51
+ };
52
+
53
+ const getClientPayload = (config) => {
54
+ const payload = {
55
+ connectType: WAProto_1.proto.ClientPayload.ConnectType.WIFI_UNKNOWN,
56
+ connectReason: WAProto_1.proto.ClientPayload.ConnectReason.USER_ACTIVATED,
57
+ userAgent: getUserAgent(config),
58
+ };
59
+ payload.webInfo = getWebInfo(config);
60
+ return payload;
61
+ };
62
+
63
+ const generateLoginNode = (userJid, config) => {
64
+ const { user, device } = (0, WABinary_1.jidDecode)(userJid);
65
+ const payload = {
66
+ ...getClientPayload(config),
67
+ passive: true,
68
+ pull: true,
69
+ username: +user,
70
+ device: device,
71
+ lidDbMigrated: false
72
+ };
73
+ return WAProto_1.proto.ClientPayload.fromObject(payload);
74
+ };
75
+ exports.generateLoginNode = generateLoginNode;
76
+
77
+ const getPlatformType = (platform) => {
78
+ const platformType = platform.toUpperCase();
79
+ return WAProto_1.proto.DeviceProps.PlatformType[platformType] || WAProto_1.proto.DeviceProps.PlatformType.CHROME;
80
+ };
81
+
82
+ const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentityKey }, config) => {
83
+ const appVersionBuf = (0, crypto_1.createHash)('md5')
84
+ .update(config.version.join('.'))
85
+ .digest();
86
+
87
+ const companion = {
88
+ os: config.browser[0],
89
+ platformType: getPlatformType(config.browser[1]),
90
+ requireFullSync: config.syncFullHistory,
91
+ historySyncConfig: {
92
+ storageQuotaMb: 10240,
93
+ inlineInitialPayloadInE2EeMsg: true,
94
+ recentSyncDaysLimit: undefined,
95
+ supportCallLogHistory: false,
96
+ supportBotUserAgentChatHistory: true,
97
+ supportCagReactionsAndPolls: true,
98
+ supportBizHostedMsg: true,
99
+ supportRecentSyncChunkMessageCountTuning: true,
100
+ supportHostedGroupMsg: true,
101
+ supportFbidBotChatHistory: true,
102
+ supportAddOnHistorySyncMigration: undefined,
103
+ supportMessageAssociation: true,
104
+ supportGroupHistory: false,
105
+ onDemandReady: undefined,
106
+ supportGuestChat: undefined
107
+ },
108
+ version: {
109
+ primary: 10,
110
+ secondary: 15,
111
+ tertiary: 7
112
+ }
113
+ };
114
+
115
+ const companionProto = WAProto_1.proto.DeviceProps.encode(companion).finish();
116
+
117
+ const registerPayload = {
118
+ ...getClientPayload(config),
119
+ passive: false,
120
+ pull: false,
121
+ devicePairingData: {
122
+ buildHash: appVersionBuf,
123
+ deviceProps: companionProto,
124
+ eRegid: (0, generics_1.encodeBigEndian)(registrationId),
125
+ eKeytype: Defaults_1.KEY_BUNDLE_TYPE,
126
+ eIdent: signedIdentityKey.public,
127
+ eSkeyId: (0, generics_1.encodeBigEndian)(signedPreKey.keyId, 3),
128
+ eSkeyVal: signedPreKey.keyPair.public,
129
+ eSkeySig: signedPreKey.signature,
130
+ },
131
+ };
132
+ return WAProto_1.proto.ClientPayload.fromObject(registerPayload);
133
+ };
134
+ exports.generateRegistrationNode = generateRegistrationNode;
135
+
136
+ const configureSuccessfulPairing = (stanza, { advSecretKey, signedIdentityKey, signalIdentities }) => {
137
+ const msgId = stanza.attrs.id;
138
+ const pairSuccessNode = (0, WABinary_1.getBinaryNodeChild)(stanza, 'pair-success');
139
+ const deviceIdentityNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'device-identity');
140
+ const platformNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'platform');
141
+ const deviceNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'device');
142
+ const businessNode = (0, WABinary_1.getBinaryNodeChild)(pairSuccessNode, 'biz');
143
+
144
+ if (!deviceIdentityNode || !deviceNode) {
145
+ throw new boom_1.Boom('Missing device-identity or device in pair success node', { data: stanza });
146
+ }
147
+
148
+ const bizName = businessNode?.attrs.name;
149
+ const jid = deviceNode.attrs.jid;
150
+ const lid = deviceNode.attrs.lid;
151
+
152
+ const { details, hmac, accountType } = WAProto_1.proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentityNode.content);
153
+
154
+ let hmacPrefix = Buffer.from([]);
155
+ if (accountType !== undefined && accountType === WAProto_1.proto.ADVEncryptionType.HOSTED) {
156
+ hmacPrefix = Buffer.from([0x06, 0x05]);
157
+ }
158
+
159
+ const advSign = (0, crypto_2.hmacSign)(Buffer.concat([hmacPrefix, details]), Buffer.from(advSecretKey, 'base64'));
160
+ if (Buffer.compare(hmac, advSign) !== 0) {
161
+ throw new boom_1.Boom('Invalid account signature');
162
+ }
163
+
164
+ const account = WAProto_1.proto.ADVSignedDeviceIdentity.decode(details);
165
+ const { accountSignatureKey, accountSignature, details: deviceDetails } = account;
166
+
167
+ const deviceIdentity = WAProto_1.proto.ADVDeviceIdentity.decode(deviceDetails);
168
+
169
+ const accountSignaturePrefix = deviceIdentity.deviceType === WAProto_1.proto.ADVEncryptionType.HOSTED
170
+ ? Buffer.from([0x06, 0x05])
171
+ : Buffer.from([0x06, 0x00]);
172
+ const accountMsg = Buffer.concat([accountSignaturePrefix, deviceDetails, signedIdentityKey.public]);
173
+
174
+ if (!crypto_2.Curve.verify(accountSignatureKey, accountMsg, accountSignature)) {
175
+ throw new boom_1.Boom('Failed to verify account signature');
176
+ }
177
+
178
+ const deviceMsg = Buffer.concat([
179
+ Buffer.from([0x06, 0x01]),
180
+ deviceDetails,
181
+ signedIdentityKey.public,
182
+ accountSignatureKey
183
+ ]);
184
+ account.deviceSignature = crypto_2.Curve.sign(signedIdentityKey.private, deviceMsg);
185
+
186
+ const identity = (0, signal_1.createSignalIdentity)(jid, accountSignatureKey);
187
+ const accountEnc = (0, exports.encodeSignedDeviceIdentity)(account, false);
188
+
189
+ const reply = {
190
+ tag: 'iq',
191
+ attrs: {
192
+ to: WABinary_1.S_WHATSAPP_NET,
193
+ type: 'result',
194
+ id: msgId,
195
+ },
196
+ content: [
197
+ {
198
+ tag: 'pair-device-sign',
199
+ attrs: {},
200
+ content: [
201
+ {
202
+ tag: 'device-identity',
203
+ attrs: { 'key-index': deviceIdentity.keyIndex.toString() },
204
+ content: accountEnc
205
+ }
206
+ ]
207
+ }
208
+ ]
209
+ };
210
+
211
+ const authUpdate = {
212
+ account,
213
+ me: { id: jid, name: bizName, lid },
214
+ signalIdentities: [
215
+ ...(signalIdentities || []),
216
+ identity
217
+ ],
218
+ platform: platformNode?.attrs.name
219
+ };
220
+
221
+ return {
222
+ creds: authUpdate,
223
+ reply
224
+ };
225
+ };
226
+ exports.configureSuccessfulPairing = configureSuccessfulPairing;
227
+
228
+ const encodeSignedDeviceIdentity = (account, includeSignatureKey) => {
229
+ account = { ...account };
230
+ if (!includeSignatureKey || !account.accountSignatureKey?.length) {
231
+ account.accountSignatureKey = null;
232
+ }
233
+ return WAProto_1.proto.ADVSignedDeviceIdentity
234
+ .encode(account)
235
+ .finish();
236
+ };
237
+ exports.encodeSignedDeviceIdentity = encodeSignedDeviceIdentity;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readAndEmitEventStream = exports.captureEventStream = void 0;
7
+ const events_1 = __importDefault(require("events"));
8
+ const fs_1 = require("fs");
9
+ const promises_1 = require("fs/promises");
10
+ const readline_1 = require("readline");
11
+ const generics_1 = require("./generics");
12
+ const make_mutex_1 = require("./make-mutex");
13
+
14
+ const captureEventStream = (ev, filename) => {
15
+ const oldEmit = ev.emit;
16
+ // write mutex so data is appended in order
17
+ const writeMutex = (0, make_mutex_1.makeMutex)();
18
+ // monkey patch eventemitter to capture all events
19
+ ev.emit = function (...args) {
20
+ const content = JSON.stringify({ timestamp: Date.now(), event: args[0], data: args[1] }) + '\n';
21
+ const result = oldEmit.apply(ev, args);
22
+ writeMutex.mutex(async () => {
23
+ await (0, promises_1.writeFile)(filename, content, { flag: 'a' });
24
+ });
25
+ return result;
26
+ };
27
+ };
28
+ exports.captureEventStream = captureEventStream;
29
+
30
+ const readAndEmitEventStream = (filename, delayIntervalMs = 0) => {
31
+ const ev = new events_1.default();
32
+ const fireEvents = async () => {
33
+ // from: https://stackoverflow.com/questions/6156501/read-a-file-one-line-at-a-time-in-node-js
34
+ const fileStream = (0, fs_1.createReadStream)(filename);
35
+ const rl = (0, readline_1.createInterface)({
36
+ input: fileStream,
37
+ crlfDelay: Infinity
38
+ });
39
+ // Note: we use the crlfDelay option to recognize all instances of CR LF
40
+ // ('\r\n') in input.txt as a single line break.
41
+ for await (const line of rl) {
42
+ if (line) {
43
+ const { event, data } = JSON.parse(line);
44
+ ev.emit(event, data);
45
+ delayIntervalMs && await (0, generics_1.delay)(delayIntervalMs);
46
+ }
47
+ }
48
+ fileStream.close();
49
+ };
50
+ return {
51
+ ev,
52
+ task: fireEvents()
53
+ };
54
+ };
55
+ exports.readAndEmitEventStream = readAndEmitEventStream;