sauruspro-bail 0.0.0-stage → 5.0.1
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 +128 -2
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6754 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +79257 -0
- package/WAProto/index.js +242946 -0
- package/docs/CHANGELOG.md +43 -0
- package/docs/NEWSLETTER-STATUS.md +120 -0
- package/docs/SPR.md +51 -0
- package/engine-requirements.js +10 -0
- package/lib/Defaults/index.js +135 -0
- package/lib/Signal/Group/ciphertext-message.js +12 -0
- package/lib/Signal/Group/group-session-builder.js +30 -0
- package/lib/Signal/Group/group_cipher.js +82 -0
- package/lib/Signal/Group/index.js +12 -0
- package/lib/Signal/Group/keyhelper.js +18 -0
- package/lib/Signal/Group/sender-chain-key.js +26 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +63 -0
- package/lib/Signal/Group/sender-key-message.js +66 -0
- package/lib/Signal/Group/sender-key-name.js +48 -0
- package/lib/Signal/Group/sender-key-record.js +41 -0
- package/lib/Signal/Group/sender-key-state.js +84 -0
- package/lib/Signal/Group/sender-message-key.js +26 -0
- package/lib/Signal/libsignal.js +431 -0
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.js +3 -0
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/MessageBuilder.js +3729 -0
- package/lib/Socket/business.js +379 -0
- package/lib/Socket/chats.js +1198 -0
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/dinonic-handler.d.ts +27 -0
- package/lib/Socket/dinonic-handler.js +882 -0
- package/lib/Socket/graphql.js +716 -0
- package/lib/Socket/groups.js +374 -0
- package/lib/Socket/index.js +96 -0
- package/lib/Socket/interop.js +463 -0
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/message-builder.js +556 -0
- package/lib/Socket/messages-recv.js +1942 -0
- package/lib/Socket/messages-send.js +1541 -0
- package/lib/Socket/mex.js +54 -0
- package/lib/Socket/newsletter.js +641 -0
- package/lib/Socket/privacy.js +318 -0
- package/lib/Socket/socket.js +1115 -0
- package/lib/Socket/username.js +236 -0
- package/lib/Store/index.js +10 -0
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +85 -0
- package/lib/Store/make-in-memory-store.js +244 -0
- package/lib/Store/make-ordered-dictionary.js +75 -0
- package/lib/Store/object-repository.js +32 -0
- package/lib/Types/Auth.js +2 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.js +2 -0
- package/lib/Types/Chat.js +8 -0
- package/lib/Types/Contact.js +2 -0
- package/lib/Types/Events.js +2 -0
- package/lib/Types/GroupMetadata.js +2 -0
- package/lib/Types/Label.js +25 -0
- package/lib/Types/LabelAssociation.js +7 -0
- package/lib/Types/Message.js +11 -0
- package/lib/Types/Mex.js +111 -0
- package/lib/Types/Product.js +2 -0
- package/lib/Types/Signal.js +2 -0
- package/lib/Types/Socket.js +3 -0
- package/lib/Types/State.js +56 -0
- package/lib/Types/USync.js +2 -0
- package/lib/Types/index.js +26 -0
- package/lib/Utils/MessageBuilder.d.ts +215 -0
- package/lib/Utils/MessageBuilder.js +3991 -0
- package/lib/Utils/a2ui.d.ts +202 -0
- package/lib/Utils/a2ui.js +732 -0
- package/lib/Utils/airich-registry.js +3 -0
- package/lib/Utils/auth-utils.js +302 -0
- package/lib/Utils/baileys-event-stream.js +58 -0
- package/lib/Utils/bot-signature.d.ts +22 -0
- package/lib/Utils/bot-signature.js +124 -0
- package/lib/Utils/browser-utils.js +48 -0
- package/lib/Utils/business.js +231 -0
- package/lib/Utils/chat-utils.js +872 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.js +118 -0
- package/lib/Utils/decode-wa-message.js +350 -0
- package/lib/Utils/dinonic-spr.d.ts +26 -0
- package/lib/Utils/dinonic-spr.js +521 -0
- package/lib/Utils/event-buffer.js +622 -0
- package/lib/Utils/extras.d.ts +449 -0
- package/lib/Utils/extras.js +1377 -0
- package/lib/Utils/generics.js +403 -0
- package/lib/Utils/history.js +134 -0
- package/lib/Utils/html-app.d.ts +39 -0
- package/lib/Utils/html-app.js +291 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.js +38 -0
- package/lib/Utils/link-preview.js +85 -0
- package/lib/Utils/logger.js +3 -0
- package/lib/Utils/lt-hash.js +8 -0
- package/lib/Utils/make-mutex.js +33 -0
- package/lib/Utils/message-composer.js +273 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/messages-media.js +870 -0
- package/lib/Utils/messages.js +1394 -0
- package/lib/Utils/metaai.d.ts +438 -0
- package/lib/Utils/metaai.js +911 -0
- package/lib/Utils/modern-messages.d.ts +150 -0
- package/lib/Utils/modern-messages.js +496 -0
- package/lib/Utils/native-flow.d.ts +14 -0
- package/lib/Utils/native-flow.js +118 -0
- package/lib/Utils/newsletter-status.d.ts +163 -0
- package/lib/Utils/newsletter-status.js +694 -0
- package/lib/Utils/noise-handler.js +201 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/payment-messages.d.ts +59 -0
- package/lib/Utils/payment-messages.js +156 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.js +630 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/scheduled-message.d.ts +82 -0
- package/lib/Utils/scheduled-message.js +113 -0
- package/lib/Utils/signal.js +201 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/status-stickers.d.ts +78 -0
- package/lib/Utils/status-stickers.js +261 -0
- package/lib/Utils/sticker.js +133 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/use-multi-file-auth-state.js +121 -0
- package/lib/Utils/use-sqlite-auth-state.js +162 -0
- package/lib/Utils/validate-connection.js +203 -0
- package/lib/WABinary/constants.js +1301 -0
- package/lib/WABinary/decode.js +262 -0
- package/lib/WABinary/encode.js +220 -0
- package/lib/WABinary/generic-utils.js +204 -0
- package/lib/WABinary/index.js +6 -0
- package/lib/WABinary/jid-utils.js +98 -0
- package/lib/WABinary/types.js +2 -0
- package/lib/WAM/BinaryInfo.js +10 -0
- package/lib/WAM/constants.js +22853 -0
- package/lib/WAM/encode.js +150 -0
- package/lib/WAM/index.js +4 -0
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +53 -0
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +54 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +58 -0
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +30 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +28 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +39 -0
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +27 -0
- package/lib/WAUSync/Protocols/index.js +12 -0
- package/lib/WAUSync/USyncQuery.js +151 -0
- package/lib/WAUSync/USyncUser.js +56 -0
- package/lib/WAUSync/index.js +3 -0
- package/lib/index.js +67 -0
- package/package.json +147 -5
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Mutex } from 'async-mutex';
|
|
2
|
+
import { mkdir, readFile, stat, unlink, writeFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { proto } from '../../WAProto/index.js';
|
|
5
|
+
import { initAuthCreds } from './auth-utils.js';
|
|
6
|
+
import { BufferJSON } from './generics.js';
|
|
7
|
+
// We need to lock files due to the fact that we are using async functions to read and write files
|
|
8
|
+
// https://github.com/WhiskeySockets/Baileys/issues/794
|
|
9
|
+
// https://github.com/nodejs/node/issues/26338
|
|
10
|
+
// Use a Map to store mutexes for each file path
|
|
11
|
+
const fileLocks = new Map();
|
|
12
|
+
// Get or create a mutex for a specific file path
|
|
13
|
+
const getFileLock = (path) => {
|
|
14
|
+
let mutex = fileLocks.get(path);
|
|
15
|
+
if (!mutex) {
|
|
16
|
+
mutex = new Mutex();
|
|
17
|
+
fileLocks.set(path, mutex);
|
|
18
|
+
}
|
|
19
|
+
return mutex;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* stores the full authentication state in a single folder.
|
|
23
|
+
* Far more efficient than singlefileauthstate
|
|
24
|
+
*
|
|
25
|
+
* Again, I wouldn't endorse this for any production level use other than perhaps a bot.
|
|
26
|
+
* Would recommend writing an auth state for use with a proper SQL or No-SQL DB
|
|
27
|
+
* */
|
|
28
|
+
export const useMultiFileAuthState = async (folder) => {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
const writeData = async (data, file) => {
|
|
31
|
+
const filePath = join(folder, fixFileName(file));
|
|
32
|
+
const mutex = getFileLock(filePath);
|
|
33
|
+
return mutex.acquire().then(async (release) => {
|
|
34
|
+
try {
|
|
35
|
+
await writeFile(filePath, JSON.stringify(data, BufferJSON.replacer));
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
release();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const readData = async (file) => {
|
|
43
|
+
try {
|
|
44
|
+
const filePath = join(folder, fixFileName(file));
|
|
45
|
+
const mutex = getFileLock(filePath);
|
|
46
|
+
return await mutex.acquire().then(async (release) => {
|
|
47
|
+
try {
|
|
48
|
+
const data = await readFile(filePath, { encoding: 'utf-8' });
|
|
49
|
+
return JSON.parse(data, BufferJSON.reviver);
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
release();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const removeData = async (file) => {
|
|
61
|
+
try {
|
|
62
|
+
const filePath = join(folder, fixFileName(file));
|
|
63
|
+
const mutex = getFileLock(filePath);
|
|
64
|
+
return mutex.acquire().then(async (release) => {
|
|
65
|
+
try {
|
|
66
|
+
await unlink(filePath);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
release();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch { }
|
|
76
|
+
};
|
|
77
|
+
const folderInfo = await stat(folder).catch(() => { });
|
|
78
|
+
if (folderInfo) {
|
|
79
|
+
if (!folderInfo.isDirectory()) {
|
|
80
|
+
throw new Error(`found something that is not a directory at ${folder}, either delete it or specify a different location`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
await mkdir(folder, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
const fixFileName = (file) => file?.replace(/\//g, '__')?.replace(/:/g, '-');
|
|
87
|
+
const creds = (await readData('creds.json')) || initAuthCreds();
|
|
88
|
+
return {
|
|
89
|
+
state: {
|
|
90
|
+
creds,
|
|
91
|
+
keys: {
|
|
92
|
+
get: async (type, ids) => {
|
|
93
|
+
const data = {};
|
|
94
|
+
await Promise.all(ids.map(async (id) => {
|
|
95
|
+
let value = await readData(`${type}-${id}.json`);
|
|
96
|
+
if (type === 'app-state-sync-key' && value) {
|
|
97
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
98
|
+
}
|
|
99
|
+
data[id] = value;
|
|
100
|
+
}));
|
|
101
|
+
return data;
|
|
102
|
+
},
|
|
103
|
+
set: async (data) => {
|
|
104
|
+
const tasks = [];
|
|
105
|
+
for (const category in data) {
|
|
106
|
+
for (const id in data[category]) {
|
|
107
|
+
const value = data[category][id];
|
|
108
|
+
const file = `${category}-${id}.json`;
|
|
109
|
+
tasks.push(value ? writeData(value, file) : removeData(file));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await Promise.all(tasks);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
saveCreds: async () => {
|
|
117
|
+
return writeData(creds, 'creds.json');
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=use-multi-file-auth-state.js.map
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile } from 'fs/promises';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { initAuthCreds } from './auth-utils.js';
|
|
5
|
+
export const useSqliteAuthState = async (pathOrFolder, options = {}) => {
|
|
6
|
+
const {
|
|
7
|
+
fileName = 'auth.db',
|
|
8
|
+
migrateFromFolder,
|
|
9
|
+
logger
|
|
10
|
+
} = options;
|
|
11
|
+
const dbPath = /\.(db|sqlite|sqlite3)$/i.test(pathOrFolder) ? pathOrFolder : join(pathOrFolder, fileName);
|
|
12
|
+
const bufReplacer = (_, value) => {
|
|
13
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
14
|
+
return {
|
|
15
|
+
type: 'Buffer',
|
|
16
|
+
data: Buffer.from(value?.data || value).toString('base64')
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
const bufReviver = (_, value) => {
|
|
22
|
+
if (value && typeof value === 'object' && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
23
|
+
return Buffer.from(value.data, 'base64');
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
const encode = value => {
|
|
28
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
29
|
+
return Buffer.concat([Buffer.from([1]), Buffer.from(value)]);
|
|
30
|
+
}
|
|
31
|
+
return Buffer.concat([Buffer.from([0]), Buffer.from(JSON.stringify(value, bufReplacer), 'utf8')]);
|
|
32
|
+
};
|
|
33
|
+
const decode = blob => {
|
|
34
|
+
if (!blob || blob.length === 0) return null;
|
|
35
|
+
if (blob[0] === 1) return Buffer.from(blob.subarray(1));
|
|
36
|
+
return JSON.parse(Buffer.from(blob.subarray(1)).toString('utf8'), bufReviver);
|
|
37
|
+
};
|
|
38
|
+
const fixName = s => s?.replace(/\//g, '__')?.replace(/:/g, '-');
|
|
39
|
+
const keyOf = (category, id) => fixName(`${category}-${id}`);
|
|
40
|
+
let DatabaseSync;
|
|
41
|
+
try {
|
|
42
|
+
({
|
|
43
|
+
DatabaseSync
|
|
44
|
+
} = await import('node:sqlite'));
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throw new Error("useSqliteAuthState needs the built-in 'node:sqlite' module (Node 22.5+). Upgrade Node, or use useMultiFileAuthState instead.");
|
|
47
|
+
}
|
|
48
|
+
await mkdir(dirname(dbPath), {
|
|
49
|
+
recursive: true
|
|
50
|
+
}).catch(() => {});
|
|
51
|
+
const db = new DatabaseSync(dbPath);
|
|
52
|
+
db.exec('PRAGMA journal_mode = TRUNCATE');
|
|
53
|
+
db.exec('PRAGMA synchronous = NORMAL');
|
|
54
|
+
db.exec('PRAGMA busy_timeout = 5000');
|
|
55
|
+
db.exec('CREATE TABLE IF NOT EXISTS auth_state (k TEXT PRIMARY KEY, v BLOB NOT NULL) WITHOUT ROWID');
|
|
56
|
+
const qGet = db.prepare('SELECT v FROM auth_state WHERE k = ?');
|
|
57
|
+
const qUpsert = db.prepare('INSERT INTO auth_state(k,v) VALUES(?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
|
|
58
|
+
const qDelete = db.prepare('DELETE FROM auth_state WHERE k = ?');
|
|
59
|
+
const runTx = fn => {
|
|
60
|
+
db.exec('BEGIN');
|
|
61
|
+
try {
|
|
62
|
+
const r = fn();
|
|
63
|
+
db.exec('COMMIT');
|
|
64
|
+
return r;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
db.exec('ROLLBACK');
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const readRaw = k => {
|
|
71
|
+
const row = qGet.get(k);
|
|
72
|
+
if (!row) return null;
|
|
73
|
+
try {
|
|
74
|
+
return decode(row.v);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
logger?.warn?.({
|
|
77
|
+
k,
|
|
78
|
+
err: err?.message
|
|
79
|
+
}, 'sqlite-auth: failed to decode row, treating as missing');
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const runMigration = async folder => {
|
|
84
|
+
let files;
|
|
85
|
+
try {
|
|
86
|
+
files = await readdir(folder);
|
|
87
|
+
} catch {
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
const rows = [];
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
if (!file.endsWith('.json')) continue;
|
|
93
|
+
let value;
|
|
94
|
+
try {
|
|
95
|
+
value = JSON.parse(await readFile(join(folder, file), 'utf8'), bufReviver);
|
|
96
|
+
} catch {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (value === null || value === undefined) continue;
|
|
100
|
+
rows.push([file.slice(0, -'.json'.length), encode(value)]);
|
|
101
|
+
}
|
|
102
|
+
runTx(() => {
|
|
103
|
+
for (const [k, v] of rows) qUpsert.run(k, v);
|
|
104
|
+
});
|
|
105
|
+
return rows.length;
|
|
106
|
+
};
|
|
107
|
+
const hasCreds = () => !!qGet.get('creds');
|
|
108
|
+
if (migrateFromFolder && !hasCreds()) {
|
|
109
|
+
const n = await runMigration(migrateFromFolder);
|
|
110
|
+
if (n > 0) logger?.info?.({
|
|
111
|
+
count: n,
|
|
112
|
+
from: migrateFromFolder
|
|
113
|
+
}, 'sqlite-auth: migrated legacy auth state');
|
|
114
|
+
}
|
|
115
|
+
let creds = readRaw('creds');
|
|
116
|
+
if (!creds) {
|
|
117
|
+
creds = initAuthCreds();
|
|
118
|
+
qUpsert.run('creds', encode(creds));
|
|
119
|
+
}
|
|
120
|
+
const keys = {
|
|
121
|
+
get: async (type, ids) => {
|
|
122
|
+
const data = {};
|
|
123
|
+
for (const id of ids) {
|
|
124
|
+
let value = readRaw(keyOf(type, id));
|
|
125
|
+
if (type === 'app-state-sync-key' && value) {
|
|
126
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
127
|
+
}
|
|
128
|
+
if (value !== null && value !== undefined) {
|
|
129
|
+
data[id] = value;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return data;
|
|
133
|
+
},
|
|
134
|
+
set: async data => {
|
|
135
|
+
const ops = [];
|
|
136
|
+
for (const category in data) {
|
|
137
|
+
for (const id in data[category]) {
|
|
138
|
+
ops.push([keyOf(category, id), data[category][id]]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
runTx(() => {
|
|
142
|
+
for (const [k, value] of ops) {
|
|
143
|
+
if (value === null || value === undefined) qDelete.run(k);else qUpsert.run(k, encode(value));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
clear: async () => {
|
|
148
|
+
db.prepare("DELETE FROM auth_state WHERE k <> 'creds'").run();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
state: {
|
|
153
|
+
creds,
|
|
154
|
+
keys
|
|
155
|
+
},
|
|
156
|
+
saveCreds: async () => {
|
|
157
|
+
qUpsert.run('creds', encode(creds));
|
|
158
|
+
},
|
|
159
|
+
db,
|
|
160
|
+
close: () => db.close()
|
|
161
|
+
};
|
|
162
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { KEY_BUNDLE_TYPE, WA_ADV_ACCOUNT_SIG_PREFIX, WA_ADV_DEVICE_SIG_PREFIX, WA_ADV_HOSTED_ACCOUNT_SIG_PREFIX } from '../Defaults/index.js';
|
|
5
|
+
import { getBinaryNodeChild, jidDecode, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
6
|
+
import { Curve, hmacSign } from './crypto.js';
|
|
7
|
+
import { encodeBigEndian } from './generics.js';
|
|
8
|
+
import { createSignalIdentity } from './signal.js';
|
|
9
|
+
const getUserAgent = (config) => {
|
|
10
|
+
return {
|
|
11
|
+
appVersion: {
|
|
12
|
+
primary: config.version[0],
|
|
13
|
+
secondary: config.version[1],
|
|
14
|
+
tertiary: config.version[2]
|
|
15
|
+
},
|
|
16
|
+
platform: proto.ClientPayload.UserAgent.Platform.MACOS,
|
|
17
|
+
releaseChannel: proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
|
|
18
|
+
osVersion: '0.1',
|
|
19
|
+
device: 'Desktop',
|
|
20
|
+
osBuildNumber: '0.1',
|
|
21
|
+
localeLanguageIso6391: 'en',
|
|
22
|
+
mnc: '000',
|
|
23
|
+
mcc: '000',
|
|
24
|
+
localeCountryIso31661Alpha2: config.countryCode
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const PLATFORM_MAP = {
|
|
28
|
+
'Mac OS': proto.ClientPayload.WebInfo.WebSubPlatform.DARWIN,
|
|
29
|
+
Windows: proto.ClientPayload.WebInfo.WebSubPlatform.WIN32
|
|
30
|
+
};
|
|
31
|
+
const getWebInfo = (config) => {
|
|
32
|
+
let webSubPlatform = proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER;
|
|
33
|
+
if (config.syncFullHistory &&
|
|
34
|
+
PLATFORM_MAP[config.browser[0]] &&
|
|
35
|
+
config.browser[1] === 'Desktop') {
|
|
36
|
+
webSubPlatform = PLATFORM_MAP[config.browser[0]];
|
|
37
|
+
}
|
|
38
|
+
return { webSubPlatform };
|
|
39
|
+
};
|
|
40
|
+
const getClientPayload = (config) => {
|
|
41
|
+
const payload = {
|
|
42
|
+
connectType: proto.ClientPayload.ConnectType.WIFI_UNKNOWN,
|
|
43
|
+
connectReason: proto.ClientPayload.ConnectReason.USER_ACTIVATED,
|
|
44
|
+
userAgent: getUserAgent(config)
|
|
45
|
+
};
|
|
46
|
+
payload.webInfo = getWebInfo(config);
|
|
47
|
+
if (config.pushName) {
|
|
48
|
+
payload.pushName = config.pushName;
|
|
49
|
+
}
|
|
50
|
+
return payload;
|
|
51
|
+
};
|
|
52
|
+
export const generateLoginNode = (userJid, config) => {
|
|
53
|
+
const { user, device } = jidDecode(userJid);
|
|
54
|
+
const payload = {
|
|
55
|
+
...getClientPayload(config),
|
|
56
|
+
passive: true,
|
|
57
|
+
pull: true,
|
|
58
|
+
username: +user,
|
|
59
|
+
device: device,
|
|
60
|
+
// TODO: investigate (hard set as false atm)
|
|
61
|
+
lidDbMigrated: false
|
|
62
|
+
};
|
|
63
|
+
return proto.ClientPayload.fromObject(payload);
|
|
64
|
+
};
|
|
65
|
+
const getPlatformType = (platform) => {
|
|
66
|
+
const platformType = platform.toUpperCase();
|
|
67
|
+
return (proto.DeviceProps.PlatformType[platformType] ||
|
|
68
|
+
proto.DeviceProps.PlatformType.CHROME);
|
|
69
|
+
};
|
|
70
|
+
export const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentityKey }, config) => {
|
|
71
|
+
// the app version needs to be md5 hashed
|
|
72
|
+
// and passed in
|
|
73
|
+
const appVersionBuf = createHash('md5')
|
|
74
|
+
.update(config.version.join('.')) // join as string
|
|
75
|
+
.digest();
|
|
76
|
+
const companion = {
|
|
77
|
+
os: config.browser[0],
|
|
78
|
+
platformType: getPlatformType(config.browser[1]),
|
|
79
|
+
requireFullSync: config.syncFullHistory,
|
|
80
|
+
historySyncConfig: {
|
|
81
|
+
storageQuotaMb: 10240,
|
|
82
|
+
inlineInitialPayloadInE2EeMsg: true,
|
|
83
|
+
recentSyncDaysLimit: undefined,
|
|
84
|
+
supportCallLogHistory: false,
|
|
85
|
+
supportBotUserAgentChatHistory: true,
|
|
86
|
+
supportCagReactionsAndPolls: true,
|
|
87
|
+
supportBizHostedMsg: true,
|
|
88
|
+
supportRecentSyncChunkMessageCountTuning: true,
|
|
89
|
+
supportHostedGroupMsg: true,
|
|
90
|
+
supportFbidBotChatHistory: true,
|
|
91
|
+
supportAddOnHistorySyncMigration: undefined,
|
|
92
|
+
supportMessageAssociation: true,
|
|
93
|
+
supportGroupHistory: false,
|
|
94
|
+
onDemandReady: undefined,
|
|
95
|
+
supportGuestChat: undefined
|
|
96
|
+
},
|
|
97
|
+
version: {
|
|
98
|
+
primary: 10,
|
|
99
|
+
secondary: 15,
|
|
100
|
+
tertiary: 7
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const companionProto = proto.DeviceProps.encode(companion).finish();
|
|
104
|
+
const registerPayload = {
|
|
105
|
+
...getClientPayload(config),
|
|
106
|
+
passive: false,
|
|
107
|
+
pull: false,
|
|
108
|
+
devicePairingData: {
|
|
109
|
+
buildHash: appVersionBuf,
|
|
110
|
+
deviceProps: companionProto,
|
|
111
|
+
eRegid: encodeBigEndian(registrationId),
|
|
112
|
+
eKeytype: KEY_BUNDLE_TYPE,
|
|
113
|
+
eIdent: signedIdentityKey.public,
|
|
114
|
+
eSkeyId: encodeBigEndian(signedPreKey.keyId, 3),
|
|
115
|
+
eSkeyVal: signedPreKey.keyPair.public,
|
|
116
|
+
eSkeySig: signedPreKey.signature
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
return proto.ClientPayload.fromObject(registerPayload);
|
|
120
|
+
};
|
|
121
|
+
export const configureSuccessfulPairing = (stanza, { advSecretKey, signedIdentityKey, signalIdentities }) => {
|
|
122
|
+
const msgId = stanza.attrs.id;
|
|
123
|
+
const pairSuccessNode = getBinaryNodeChild(stanza, 'pair-success');
|
|
124
|
+
const deviceIdentityNode = getBinaryNodeChild(pairSuccessNode, 'device-identity');
|
|
125
|
+
const platformNode = getBinaryNodeChild(pairSuccessNode, 'platform');
|
|
126
|
+
const deviceNode = getBinaryNodeChild(pairSuccessNode, 'device');
|
|
127
|
+
const businessNode = getBinaryNodeChild(pairSuccessNode, 'biz');
|
|
128
|
+
if (!deviceIdentityNode || !deviceNode) {
|
|
129
|
+
throw new Boom('Missing device-identity or device in pair success node', { data: stanza });
|
|
130
|
+
}
|
|
131
|
+
const bizName = businessNode?.attrs.name;
|
|
132
|
+
const jid = deviceNode.attrs.jid;
|
|
133
|
+
const lid = deviceNode.attrs.lid;
|
|
134
|
+
const { details, hmac, accountType } = proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentityNode.content);
|
|
135
|
+
let hmacPrefix = Buffer.from([]);
|
|
136
|
+
if (accountType !== undefined && accountType === proto.ADVEncryptionType.HOSTED) {
|
|
137
|
+
hmacPrefix = WA_ADV_HOSTED_ACCOUNT_SIG_PREFIX;
|
|
138
|
+
}
|
|
139
|
+
const advSign = hmacSign(Buffer.concat([hmacPrefix, details]), Buffer.from(advSecretKey, 'base64'));
|
|
140
|
+
if (Buffer.compare(hmac, advSign) !== 0) {
|
|
141
|
+
throw new Boom('Invalid account signature');
|
|
142
|
+
}
|
|
143
|
+
const account = proto.ADVSignedDeviceIdentity.decode(details);
|
|
144
|
+
const { accountSignatureKey, accountSignature, details: deviceDetails } = account;
|
|
145
|
+
const deviceIdentity = proto.ADVDeviceIdentity.decode(deviceDetails);
|
|
146
|
+
const accountSignaturePrefix = deviceIdentity.deviceType === proto.ADVEncryptionType.HOSTED
|
|
147
|
+
? WA_ADV_HOSTED_ACCOUNT_SIG_PREFIX
|
|
148
|
+
: WA_ADV_ACCOUNT_SIG_PREFIX;
|
|
149
|
+
const accountMsg = Buffer.concat([accountSignaturePrefix, deviceDetails, signedIdentityKey.public]);
|
|
150
|
+
if (!Curve.verify(accountSignatureKey, accountMsg, accountSignature)) {
|
|
151
|
+
throw new Boom('Failed to verify account signature');
|
|
152
|
+
}
|
|
153
|
+
const deviceMsg = Buffer.concat([
|
|
154
|
+
WA_ADV_DEVICE_SIG_PREFIX,
|
|
155
|
+
deviceDetails,
|
|
156
|
+
signedIdentityKey.public,
|
|
157
|
+
accountSignatureKey
|
|
158
|
+
]);
|
|
159
|
+
account.deviceSignature = Curve.sign(signedIdentityKey.private, deviceMsg);
|
|
160
|
+
const identity = createSignalIdentity(lid, accountSignatureKey);
|
|
161
|
+
const accountEnc = encodeSignedDeviceIdentity(account, false);
|
|
162
|
+
const reply = {
|
|
163
|
+
tag: 'iq',
|
|
164
|
+
attrs: {
|
|
165
|
+
to: S_WHATSAPP_NET,
|
|
166
|
+
type: 'result',
|
|
167
|
+
id: msgId
|
|
168
|
+
},
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
tag: 'pair-device-sign',
|
|
172
|
+
attrs: {},
|
|
173
|
+
content: [
|
|
174
|
+
{
|
|
175
|
+
tag: 'device-identity',
|
|
176
|
+
attrs: { 'key-index': deviceIdentity.keyIndex.toString() },
|
|
177
|
+
content: accountEnc
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
};
|
|
183
|
+
const authUpdate = {
|
|
184
|
+
account,
|
|
185
|
+
me: { id: jid, name: bizName, lid },
|
|
186
|
+
signalIdentities: [...(signalIdentities || []), identity],
|
|
187
|
+
platform: platformNode?.attrs.name
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
creds: authUpdate,
|
|
191
|
+
reply
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
export const encodeSignedDeviceIdentity = (account, includeSignatureKey) => {
|
|
195
|
+
account = { ...account };
|
|
196
|
+
// set to null if we are not to include the signature key
|
|
197
|
+
// or if we are including the signature key but it is empty
|
|
198
|
+
if (!includeSignatureKey || !account.accountSignatureKey?.length) {
|
|
199
|
+
account.accountSignatureKey = null;
|
|
200
|
+
}
|
|
201
|
+
return proto.ADVSignedDeviceIdentity.encode(account).finish();
|
|
202
|
+
};
|
|
203
|
+
//# sourceMappingURL=validate-connection.js.map
|