@nerxa/socketon 0.0.0-stage → 1.21.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/EXPERIMENTAL.md +350 -0
- package/LICENSE +24 -0
- package/README.md +6317 -2
- package/WAProto/index.d.ts +17301 -0
- package/WAProto/index.js +125088 -0
- package/engine-requirements.js +14 -0
- package/package.json +125 -4
- package/src/Defaults/index.d.ts +152 -0
- package/src/Defaults/index.js +164 -0
- package/src/MessageBuilder/bot-signature.d.ts +22 -0
- package/src/MessageBuilder/bot-signature.js +122 -0
- package/src/MessageBuilder/extras.d.ts +449 -0
- package/src/MessageBuilder/extras.js +1356 -0
- package/src/MessageBuilder/index.d.ts +210 -0
- package/src/MessageBuilder/index.js +3594 -0
- package/src/MessageBuilder/metaai.d.ts +438 -0
- package/src/MessageBuilder/metaai.js +890 -0
- package/src/Signal/Group/ciphertext-message.d.ts +9 -0
- package/src/Signal/Group/ciphertext-message.js +11 -0
- package/src/Signal/Group/group-session-builder.d.ts +7 -0
- package/src/Signal/Group/group-session-builder.js +27 -0
- package/src/Signal/Group/group_cipher.d.ts +11 -0
- package/src/Signal/Group/group_cipher.js +78 -0
- package/src/Signal/Group/index.d.ts +11 -0
- package/src/Signal/Group/index.js +11 -0
- package/src/Signal/Group/keyhelper.d.ts +6 -0
- package/src/Signal/Group/keyhelper.js +17 -0
- package/src/Signal/Group/sender-chain-key.d.ts +13 -0
- package/src/Signal/Group/sender-chain-key.js +25 -0
- package/src/Signal/Group/sender-key-distribution-message.d.ts +16 -0
- package/src/Signal/Group/sender-key-distribution-message.js +62 -0
- package/src/Signal/Group/sender-key-message.d.ts +18 -0
- package/src/Signal/Group/sender-key-message.js +65 -0
- package/src/Signal/Group/sender-key-name.d.ts +11 -0
- package/src/Signal/Group/sender-key-name.js +47 -0
- package/src/Signal/Group/sender-key-record.d.ts +12 -0
- package/src/Signal/Group/sender-key-record.js +40 -0
- package/src/Signal/Group/sender-key-state.d.ts +16 -0
- package/src/Signal/Group/sender-key-state.js +83 -0
- package/src/Signal/Group/sender-message-key.d.ts +11 -0
- package/src/Signal/Group/sender-message-key.js +25 -0
- package/src/Signal/libsignal.d.ts +54 -0
- package/src/Signal/libsignal.js +427 -0
- package/src/Signal/lid-mapping.d.ts +20 -0
- package/src/Signal/lid-mapping.js +274 -0
- package/src/Socket/Client/index.d.ts +2 -0
- package/src/Socket/Client/index.js +3 -0
- package/src/Socket/Client/tcp.js +177 -0
- package/src/Socket/Client/types.d.ts +5 -0
- package/src/Socket/Client/types.js +9 -0
- package/src/Socket/Client/websocket.d.ts +12 -0
- package/src/Socket/Client/websocket.js +53 -0
- package/src/Socket/business.d.ts +431 -0
- package/src/Socket/business.js +379 -0
- package/src/Socket/chats.d.ts +247 -0
- package/src/Socket/chats.js +1484 -0
- package/src/Socket/communities.d.ts +598 -0
- package/src/Socket/communities.js +421 -0
- package/src/Socket/email.js +145 -0
- package/src/Socket/groups.d.ts +298 -0
- package/src/Socket/groups.js +339 -0
- package/src/Socket/index.d.ts +581 -0
- package/src/Socket/index.js +77 -0
- package/src/Socket/messages-recv.d.ts +370 -0
- package/src/Socket/messages-recv.js +1864 -0
- package/src/Socket/messages-send.d.ts +390 -0
- package/src/Socket/messages-send.js +1574 -0
- package/src/Socket/mex.d.ts +1 -0
- package/src/Socket/mex.js +44 -0
- package/src/Socket/mobile-builders.js +72 -0
- package/src/Socket/mobile.js +424 -0
- package/src/Socket/newsletter.d.ts +468 -0
- package/src/Socket/newsletter.js +671 -0
- package/src/Socket/socket.d.ts +77 -0
- package/src/Socket/socket.js +1106 -0
- package/src/Store/index.d.ts +3 -0
- package/src/Store/index.js +3 -0
- package/src/Store/make-in-memory-store.d.ts +62 -0
- package/src/Store/make-in-memory-store.js +396 -0
- package/src/Store/make-ordered-dictionary.d.ts +12 -0
- package/src/Store/make-ordered-dictionary.js +78 -0
- package/src/Store/object-repository.d.ts +10 -0
- package/src/Store/object-repository.js +23 -0
- package/src/Types/Auth.d.ts +1 -0
- package/src/Types/Auth.js +1 -0
- package/src/Types/Bussines.d.ts +1 -0
- package/src/Types/Bussines.js +1 -0
- package/src/Types/Call.d.ts +1 -0
- package/src/Types/Call.js +1 -0
- package/src/Types/Chat.d.ts +2 -0
- package/src/Types/Chat.js +24 -0
- package/src/Types/Contact.d.ts +1 -0
- package/src/Types/Contact.js +1 -0
- package/src/Types/Events.d.ts +1 -0
- package/src/Types/Events.js +0 -0
- package/src/Types/GroupMetadata.d.ts +5 -0
- package/src/Types/GroupMetadata.js +6 -0
- package/src/Types/Label.d.ts +2 -0
- package/src/Types/Label.js +24 -0
- package/src/Types/LabelAssociation.d.ts +2 -0
- package/src/Types/LabelAssociation.js +6 -0
- package/src/Types/Message.d.ts +13 -0
- package/src/Types/Message.js +23 -0
- package/src/Types/Mex.d.ts +2 -0
- package/src/Types/Mex.js +95 -0
- package/src/Types/Product.d.ts +1 -0
- package/src/Types/Product.js +1 -0
- package/src/Types/Signal.d.ts +1 -0
- package/src/Types/Signal.js +0 -0
- package/src/Types/Socket.d.ts +1 -0
- package/src/Types/Socket.js +0 -0
- package/src/Types/State.d.ts +5 -0
- package/src/Types/State.js +54 -0
- package/src/Types/USync.d.ts +1 -0
- package/src/Types/USync.js +0 -0
- package/src/Types/index.d.ts +13 -0
- package/src/Types/index.js +25 -0
- package/src/Utils/auth-store.d.ts +29 -0
- package/src/Utils/auth-store.js +76 -0
- package/src/Utils/auth-utils.d.ts +58 -0
- package/src/Utils/auth-utils.js +273 -0
- package/src/Utils/browser-utils.d.ts +9 -0
- package/src/Utils/browser-utils.js +28 -0
- package/src/Utils/business.d.ts +49 -0
- package/src/Utils/business.js +226 -0
- package/src/Utils/chat-utils.d.ts +408 -0
- package/src/Utils/chat-utils.js +846 -0
- package/src/Utils/companion-reg-client-utils.d.ts +4 -0
- package/src/Utils/companion-reg-client-utils.js +39 -0
- package/src/Utils/crypto.d.ts +39 -0
- package/src/Utils/crypto.js +110 -0
- package/src/Utils/decode-wa-message.d.ts +99 -0
- package/src/Utils/decode-wa-message.js +373 -0
- package/src/Utils/event-buffer.d.ts +22 -0
- package/src/Utils/event-buffer.js +624 -0
- package/src/Utils/generics.d.ts +63 -0
- package/src/Utils/generics.js +375 -0
- package/src/Utils/group-metadata-cache.d.ts +4 -0
- package/src/Utils/group-metadata-cache.js +25 -0
- package/src/Utils/history.d.ts +75 -0
- package/src/Utils/history.js +131 -0
- package/src/Utils/html-app.d.ts +19 -0
- package/src/Utils/html-app.js +96 -0
- package/src/Utils/html-multiplayer.d.ts +10 -0
- package/src/Utils/html-multiplayer.js +94 -0
- package/src/Utils/identity-change-handler.d.ts +13 -0
- package/src/Utils/identity-change-handler.js +48 -0
- package/src/Utils/index.d.ts +44 -0
- package/src/Utils/index.js +44 -0
- package/src/Utils/link-preview-metadata.d.ts +47 -0
- package/src/Utils/link-preview-metadata.js +88 -0
- package/src/Utils/link-preview.d.ts +12 -0
- package/src/Utils/link-preview.js +79 -0
- package/src/Utils/logger.d.ts +2 -0
- package/src/Utils/logger.js +2 -0
- package/src/Utils/lt-hash.d.ts +6 -0
- package/src/Utils/lt-hash.js +3 -0
- package/src/Utils/make-mutex.d.ts +6 -0
- package/src/Utils/make-mutex.js +32 -0
- package/src/Utils/message-retry-manager.d.ts +79 -0
- package/src/Utils/message-retry-manager.js +255 -0
- package/src/Utils/messages-media.d.ts +136 -0
- package/src/Utils/messages-media.js +814 -0
- package/src/Utils/messages.d.ts +62 -0
- package/src/Utils/messages.js +2223 -0
- package/src/Utils/mobile-companion-crypto.js +191 -0
- package/src/Utils/modern-messages.d.ts +150 -0
- package/src/Utils/modern-messages.js +496 -0
- package/src/Utils/native-flow.d.ts +11 -0
- package/src/Utils/native-flow.js +115 -0
- package/src/Utils/newsletter-status.d.ts +158 -0
- package/src/Utils/newsletter-status.js +651 -0
- package/src/Utils/noise-handler.d.ts +20 -0
- package/src/Utils/noise-handler.js +200 -0
- package/src/Utils/offline-node-processor.d.ts +9 -0
- package/src/Utils/offline-node-processor.js +33 -0
- package/src/Utils/optional-media.d.ts +3 -0
- package/src/Utils/optional-media.js +45 -0
- package/src/Utils/payment-messages.d.ts +66 -0
- package/src/Utils/payment-messages.js +160 -0
- package/src/Utils/pre-key-manager.d.ts +25 -0
- package/src/Utils/pre-key-manager.js +95 -0
- package/src/Utils/process-message.d.ts +60 -0
- package/src/Utils/process-message.js +688 -0
- package/src/Utils/reporting-utils.d.ts +12 -0
- package/src/Utils/reporting-utils.js +256 -0
- package/src/Utils/scheduled-message.d.ts +51 -0
- package/src/Utils/scheduled-message.js +112 -0
- package/src/Utils/sender-key-memory.d.ts +19 -0
- package/src/Utils/sender-key-memory.js +51 -0
- package/src/Utils/signal.d.ts +82 -0
- package/src/Utils/signal.js +193 -0
- package/src/Utils/stanza-ack.d.ts +16 -0
- package/src/Utils/stanza-ack.js +30 -0
- package/src/Utils/status-stickers.d.ts +127 -0
- package/src/Utils/status-stickers.js +260 -0
- package/src/Utils/sync-action-utils.d.ts +2 -0
- package/src/Utils/sync-action-utils.js +44 -0
- package/src/Utils/tc-token-utils.d.ts +29 -0
- package/src/Utils/tc-token-utils.js +155 -0
- package/src/Utils/ttl-cache.d.ts +28 -0
- package/src/Utils/ttl-cache.js +120 -0
- package/src/Utils/use-mongo-auth-state.d.ts +21 -0
- package/src/Utils/use-mongo-auth-state.js +67 -0
- package/src/Utils/use-multi-file-auth-state.d.ts +11 -0
- package/src/Utils/use-multi-file-auth-state.js +106 -0
- package/src/Utils/use-mysql-auth-state.d.ts +19 -0
- package/src/Utils/use-mysql-auth-state.js +79 -0
- package/src/Utils/use-postgres-auth-state.d.ts +19 -0
- package/src/Utils/use-postgres-auth-state.js +79 -0
- package/src/Utils/use-redis-auth-state.d.ts +19 -0
- package/src/Utils/use-redis-auth-state.js +89 -0
- package/src/Utils/use-single-file-auth-state.d.ts +10 -0
- package/src/Utils/use-single-file-auth-state.js +108 -0
- package/src/Utils/use-sqlite-auth-state.d.ts +12 -0
- package/src/Utils/use-sqlite-auth-state.js +120 -0
- package/src/Utils/useNekoDBAuth.d.ts +19 -0
- package/src/Utils/useNekoDBAuth.js +111 -0
- package/src/Utils/username.d.ts +12 -0
- package/src/Utils/username.js +58 -0
- package/src/Utils/validate-connection.d.ts +43 -0
- package/src/Utils/validate-connection.js +277 -0
- package/src/Utils/voice-recognition.d.ts +53 -0
- package/src/Utils/voice-recognition.js +219 -0
- package/src/Voip/audio-feeder.d.ts +24 -0
- package/src/Voip/audio-feeder.js +268 -0
- package/src/Voip/index.d.ts +88 -0
- package/src/Voip/index.js +936 -0
- package/src/Voip/relay-transport.d.ts +5 -0
- package/src/Voip/relay-transport.js +824 -0
- package/src/Voip/signaling.d.ts +11 -0
- package/src/Voip/signaling.js +1099 -0
- package/src/Voip/types.d.ts +2 -0
- package/src/Voip/types.js +12 -0
- package/src/Voip/video-feeder.d.ts +24 -0
- package/src/Voip/video-feeder.js +268 -0
- package/src/Voip/wasm-engine.d.ts +26 -0
- package/src/Voip/wasm-engine.js +1307 -0
- package/src/Voip/worker-bootstrap.js +1023 -0
- package/src/WABinary/constants.d.ts +22 -0
- package/src/WABinary/constants.js +1300 -0
- package/src/WABinary/decode.d.ts +9 -0
- package/src/WABinary/decode.js +261 -0
- package/src/WABinary/encode.d.ts +2 -0
- package/src/WABinary/encode.js +219 -0
- package/src/WABinary/generic-utils.d.ts +76 -0
- package/src/WABinary/generic-utils.js +214 -0
- package/src/WABinary/index.d.ts +5 -0
- package/src/WABinary/index.js +5 -0
- package/src/WABinary/jid-utils.d.ts +30 -0
- package/src/WABinary/jid-utils.js +97 -0
- package/src/WABinary/types.d.ts +1 -0
- package/src/WABinary/types.js +0 -0
- package/src/WAM/BinaryInfo.d.ts +7 -0
- package/src/WAM/BinaryInfo.js +9 -0
- package/src/WAM/constants.d.ts +34926 -0
- package/src/WAM/constants.js +22852 -0
- package/src/WAM/encode.d.ts +1 -0
- package/src/WAM/encode.js +148 -0
- package/src/WAM/index.d.ts +3 -0
- package/src/WAM/index.js +3 -0
- package/src/WAUSync/Protocols/USyncContactProtocol.d.ts +36 -0
- package/src/WAUSync/Protocols/USyncContactProtocol.js +50 -0
- package/src/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
- package/src/WAUSync/Protocols/USyncDeviceProtocol.js +51 -0
- package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
- package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
- package/src/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
- package/src/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
- package/src/WAUSync/Protocols/USyncUsernameProtocol.d.ts +9 -0
- package/src/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
- package/src/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +38 -0
- package/src/WAUSync/Protocols/UsyncBotProfileProtocol.js +49 -0
- package/src/WAUSync/Protocols/UsyncLIDProtocol.d.ts +14 -0
- package/src/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
- package/src/WAUSync/Protocols/index.d.ts +5 -0
- package/src/WAUSync/Protocols/index.js +5 -0
- package/src/WAUSync/USyncQuery.d.ts +20 -0
- package/src/WAUSync/USyncQuery.js +93 -0
- package/src/WAUSync/USyncUser.d.ts +16 -0
- package/src/WAUSync/USyncUser.js +30 -0
- package/src/WAUSync/index.d.ts +3 -0
- package/src/WAUSync/index.js +3 -0
- package/src/assets/wasm/README.md +76 -0
- package/src/assets/wasm/loader.js +5 -0
- package/src/assets/wasm/whatsapp.wasm +0 -0
- package/src/assets/wasm/worker-modules.js +273 -0
- package/src/index.d.ts +43 -0
- package/src/index.js +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function executeWMexQuery(variables: any, queryId: any, dataPath: any, query: any, generateMessageTag: any): Promise<any>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { getBinaryNodeChild, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
3
|
+
const wMexQuery = (variables, queryId, query, generateMessageTag) => {
|
|
4
|
+
return query({
|
|
5
|
+
tag: 'iq',
|
|
6
|
+
attrs: {
|
|
7
|
+
id: generateMessageTag(),
|
|
8
|
+
type: 'get',
|
|
9
|
+
to: S_WHATSAPP_NET,
|
|
10
|
+
xmlns: 'w:mex'
|
|
11
|
+
},
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
tag: 'query',
|
|
15
|
+
attrs: { query_id: queryId },
|
|
16
|
+
content: Buffer.from(JSON.stringify({ variables }), 'utf-8')
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
export const executeWMexQuery = async (variables, queryId, dataPath, query, generateMessageTag) => {
|
|
22
|
+
const result = await wMexQuery(variables, queryId, query, generateMessageTag);
|
|
23
|
+
const child = getBinaryNodeChild(result, 'result');
|
|
24
|
+
if (child?.content) {
|
|
25
|
+
const data = JSON.parse(child.content.toString());
|
|
26
|
+
if (data.errors && data.errors.length > 0) {
|
|
27
|
+
const errorMessages = data.errors.map((err) => err.message || 'Unknown error').join(', ');
|
|
28
|
+
const firstError = data.errors[0];
|
|
29
|
+
const errorCode = firstError.extensions?.error_code || 400;
|
|
30
|
+
throw new Boom(`GraphQL server error: ${errorMessages} (${dataPath || queryId})`, {
|
|
31
|
+
statusCode: errorCode,
|
|
32
|
+
data: { ...firstError, queryId, sent: variables }
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const response = dataPath ? data?.data?.[dataPath] : data?.data;
|
|
36
|
+
if (typeof response !== 'undefined') {
|
|
37
|
+
return response;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const action = (dataPath || '').startsWith('xwa2_')
|
|
41
|
+
? dataPath.substring(5).replace(/_/g, ' ')
|
|
42
|
+
: dataPath?.replace(/_/g, ' ');
|
|
43
|
+
throw new Boom(`Failed to ${action}, unexpected response structure.`, { statusCode: 400, data: result });
|
|
44
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { proto } from '../../WAProto/index.js';
|
|
2
|
+
import { S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
3
|
+
|
|
4
|
+
const buildIqNode = (type, to, xmlns, content) => ({
|
|
5
|
+
tag: 'iq',
|
|
6
|
+
attrs: { to: to || S_WHATSAPP_NET, type, xmlns },
|
|
7
|
+
content
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const buildPairDeviceIq = (input) => {
|
|
11
|
+
const children = [
|
|
12
|
+
{ tag: 'ref', attrs: {}, content: input.ref },
|
|
13
|
+
{ tag: 'pub-key', attrs: {}, content: input.companionNoisePublicKey },
|
|
14
|
+
{ tag: 'device-identity', attrs: {}, content: input.deviceIdentityBytes },
|
|
15
|
+
{ tag: 'key-index-list', attrs: { ts: String(input.keyIndexListTimestampSeconds) }, content: input.keyIndexListBytes }
|
|
16
|
+
];
|
|
17
|
+
if (input.clientProps) {
|
|
18
|
+
children.push({
|
|
19
|
+
tag: 'client-props',
|
|
20
|
+
attrs: {},
|
|
21
|
+
content: proto.ClientPairingProps.encode({
|
|
22
|
+
isChatDbLidMigrated: input.clientProps.isChatDbLidMigrated,
|
|
23
|
+
isSyncdPureLidSession: input.clientProps.isSyncdPureLidSession,
|
|
24
|
+
isSyncdSnapshotRecoveryEnabled: input.clientProps.isSyncdSnapshotRecoveryEnabled
|
|
25
|
+
}).finish()
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (input.pem) {
|
|
29
|
+
children.push({
|
|
30
|
+
tag: 'pem',
|
|
31
|
+
attrs: { version: '1', algorithm: 'rsa2048' },
|
|
32
|
+
content: [
|
|
33
|
+
{ tag: 'pem', attrs: {}, content: input.pem.aesGcmKeyBytes },
|
|
34
|
+
{ tag: 'ttl', attrs: { ts_s: String(input.pem.ttlSeconds) } },
|
|
35
|
+
{ tag: 'key_id', attrs: {}, content: '1' }
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return buildIqNode('set', S_WHATSAPP_NET, 'md', [
|
|
40
|
+
{ tag: 'pair-device', attrs: {}, content: children }
|
|
41
|
+
]);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const buildKeyIndexListPublishIq = (input) => buildIqNode('set', S_WHATSAPP_NET, 'md', [
|
|
45
|
+
{ tag: 'key-index-list', attrs: { ts: String(input.timestampSeconds) }, content: input.keyIndexListBytes }
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
export const buildRemoveCompanionDeviceIq = (input) => buildIqNode('set', S_WHATSAPP_NET, 'md', [
|
|
49
|
+
{ tag: 'remove-companion-device', attrs: { jid: input.deviceJid, reason: input.reason } }
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
export const buildRemoveAllCompanionDevicesIq = (input) => {
|
|
53
|
+
const attrs = { all: 'true', reason: input.reason };
|
|
54
|
+
if (input.excludeHostedCompanion) attrs.exclude_hosted_companion = 'true';
|
|
55
|
+
return buildIqNode('set', S_WHATSAPP_NET, 'md', [
|
|
56
|
+
{ tag: 'remove-companion-device', attrs }
|
|
57
|
+
]);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const buildPrimaryHelloIq = (input) => buildIqNode('set', S_WHATSAPP_NET, 'md', [
|
|
61
|
+
{
|
|
62
|
+
tag: 'link_code_companion_reg',
|
|
63
|
+
attrs: { stage: 'primary_hello' },
|
|
64
|
+
content: [
|
|
65
|
+
{ tag: 'link_code_pairing_wrapped_primary_ephemeral_pub', attrs: {}, content: input.wrappedPrimaryEphemeralPub },
|
|
66
|
+
{ tag: 'primary_identity_pub', attrs: {}, content: input.primaryIdentityPub },
|
|
67
|
+
{ tag: 'link_code_pairing_ref', attrs: {}, content: input.ref }
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
5
|
+
import { getBinaryNodeChild, getAllBinaryNodeChildren, getBinaryNodeChildren } from '../WABinary/index.js';
|
|
6
|
+
import {
|
|
7
|
+
parseCompanionQr,
|
|
8
|
+
buildSignedCompanionIdentity,
|
|
9
|
+
buildSignedKeyIndexList,
|
|
10
|
+
preparePrimaryHello,
|
|
11
|
+
completePrimaryHandshake,
|
|
12
|
+
derivePemKey,
|
|
13
|
+
buildHistorySyncBootstrapMessage
|
|
14
|
+
} from '../Utils/mobile-companion-crypto.js';
|
|
15
|
+
import {
|
|
16
|
+
buildPairDeviceIq,
|
|
17
|
+
buildKeyIndexListPublishIq,
|
|
18
|
+
buildRemoveCompanionDeviceIq,
|
|
19
|
+
buildRemoveAllCompanionDevicesIq,
|
|
20
|
+
buildPrimaryHelloIq
|
|
21
|
+
} from './mobile-builders.js';
|
|
22
|
+
|
|
23
|
+
const PAIR_DEVICE_TIMEOUT_MS = 32000;
|
|
24
|
+
const KEY_INDEX_TIMEOUT_MS = 32000;
|
|
25
|
+
const REMOVE_DEVICE_TIMEOUT_MS = 32000;
|
|
26
|
+
const PRIMARY_HELLO_TIMEOUT_MS = 120000;
|
|
27
|
+
const LINK_CODE_FINISH_TIMEOUT_MS = 120000;
|
|
28
|
+
const PEM_TTL_SECONDS = 5 * 24 * 60 * 60;
|
|
29
|
+
|
|
30
|
+
const toRawPubKey = (pub) => {
|
|
31
|
+
const b = Buffer.from(pub);
|
|
32
|
+
return b.length === 33 ? b.slice(1) : b;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const getNodeTextContent = (node) => {
|
|
36
|
+
if (!node) return null;
|
|
37
|
+
if (typeof node.content === 'string') return node.content;
|
|
38
|
+
if (Buffer.isBuffer(node.content)) return node.content.toString('utf8');
|
|
39
|
+
if (Array.isArray(node.content)) return node.content.map(getNodeTextContent).filter(Boolean).join('');
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const getNodeBytesContent = (node) => {
|
|
44
|
+
if (!node) return null;
|
|
45
|
+
if (Buffer.isBuffer(node.content)) return new Uint8Array(node.content);
|
|
46
|
+
if (node.content instanceof Uint8Array) return node.content;
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const findNodeChild = (parent, tag) => {
|
|
51
|
+
if (!parent) return undefined;
|
|
52
|
+
const children = Array.isArray(parent.content) ? parent.content : (parent.content ? [parent.content] : []);
|
|
53
|
+
return children.find((c) => c && c.tag === tag);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const makeMobileCoordinator = (sock, config) => {
|
|
57
|
+
const logger = sock.logger || console;
|
|
58
|
+
const ev = sock.ev;
|
|
59
|
+
const query = (node, timeout) => sock.query(node, timeout);
|
|
60
|
+
const authState = sock.authState;
|
|
61
|
+
const persistence = config?.companionHost?.persistence;
|
|
62
|
+
|
|
63
|
+
let epoch = null;
|
|
64
|
+
let pendingCompanionHello = null;
|
|
65
|
+
let pendingFinish = null;
|
|
66
|
+
const accountKeyIndexes = new Set([0]);
|
|
67
|
+
|
|
68
|
+
const isMobilePrimary = () => (typeof config.isMobilePrimary === 'function' ? config.isMobilePrimary() : !!authState?.creds?.deviceInfo);
|
|
69
|
+
|
|
70
|
+
const requirePrimaryIdentityKeyPair = () => {
|
|
71
|
+
if (!isMobilePrimary()) throw new Error('client.mobile requires a mobile-primary session');
|
|
72
|
+
const creds = authState.creds;
|
|
73
|
+
if (!creds?.signedIdentityKey) throw new Error('companion-host requires signedIdentityKey');
|
|
74
|
+
return creds.signedIdentityKey;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const ensureEpoch = async () => {
|
|
78
|
+
if (epoch) return epoch;
|
|
79
|
+
if (persistence) {
|
|
80
|
+
try {
|
|
81
|
+
const loaded = await persistence.load();
|
|
82
|
+
if (loaded) { epoch = loaded; return epoch; }
|
|
83
|
+
} catch (e) { logger.warn?.({ e }, 'companion epoch load failed'); }
|
|
84
|
+
}
|
|
85
|
+
epoch = { rawId: Math.floor(Math.random() * 0x7fffffff) + 1, currentKeyIndex: 0, companions: [] };
|
|
86
|
+
if (persistence) {
|
|
87
|
+
try { await persistence.save(epoch); } catch (e) { logger.warn?.({ e }, 'companion epoch save failed'); }
|
|
88
|
+
}
|
|
89
|
+
return epoch;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const persist = async (state) => {
|
|
93
|
+
if (persistence) {
|
|
94
|
+
try { await persistence.save(state); } catch (e) { logger.warn?.({ e }, 'companion persist failed'); }
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const validKeyIndexes = (ep) => {
|
|
99
|
+
const indexes = new Set([0, ...accountKeyIndexes]);
|
|
100
|
+
for (const c of (ep.companions || [])) indexes.add(c.keyIndex);
|
|
101
|
+
return [...indexes].sort((a, b) => a - b);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const nextKeyIndex = (ep) => Math.max(ep.currentKeyIndex || 0, ...validKeyIndexes(ep)) + 1;
|
|
105
|
+
|
|
106
|
+
const emit = (event, payload) => {
|
|
107
|
+
try { ev.emit(event, payload); } catch (e) { logger.warn?.({ e }, 'emit failed'); }
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const buildClientPairingProps = () => ({
|
|
111
|
+
isChatDbLidMigrated: true,
|
|
112
|
+
isSyncdPureLidSession: true,
|
|
113
|
+
isSyncdSnapshotRecoveryEnabled: true
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const handleIncomingNotification = (node) => {
|
|
117
|
+
try {
|
|
118
|
+
if (node?.attrs?.type === 'account_sync') {
|
|
119
|
+
const devices = findNodeChild(node, 'devices');
|
|
120
|
+
if (devices) {
|
|
121
|
+
const indexes = new Set([0]);
|
|
122
|
+
for (const child of getBinaryNodeChildren(devices)) {
|
|
123
|
+
if (child.tag !== 'device') continue;
|
|
124
|
+
const rawIndex = child.attrs?.['key-index'];
|
|
125
|
+
const parsed = rawIndex ? parseInt(rawIndex, 10) : NaN;
|
|
126
|
+
if (Number.isInteger(parsed) && parsed >= 0) indexes.add(parsed);
|
|
127
|
+
}
|
|
128
|
+
accountKeyIndexes.clear();
|
|
129
|
+
for (const i of indexes) accountKeyIndexes.add(i);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const linkCode = findNodeChild(node, 'link_code_companion_reg');
|
|
133
|
+
if (linkCode) {
|
|
134
|
+
const stage = linkCode.attrs?.stage;
|
|
135
|
+
if (stage === 'companion_hello') {
|
|
136
|
+
const ref = getNodeTextContent(findNodeChild(linkCode, 'link_code_pairing_ref'));
|
|
137
|
+
const wrapped = getNodeBytesContent(findNodeChild(linkCode, 'link_code_pairing_wrapped_companion_ephemeral_pub'));
|
|
138
|
+
const authKey = getNodeBytesContent(findNodeChild(linkCode, 'companion_server_auth_key_pub'));
|
|
139
|
+
if (ref && wrapped && authKey) {
|
|
140
|
+
pendingCompanionHello = { ref, wrappedCompanionEphemeralPub: wrapped, companionServerAuthKeyPub: authKey };
|
|
141
|
+
}
|
|
142
|
+
} else if (stage === 'companion_finish') {
|
|
143
|
+
const ref = getNodeTextContent(findNodeChild(linkCode, 'link_code_pairing_ref'));
|
|
144
|
+
const wrappedKeyBundle = getNodeBytesContent(findNodeChild(linkCode, 'link_code_pairing_wrapped_key_bundle'));
|
|
145
|
+
const companionIdentityPub = getNodeBytesContent(findNodeChild(linkCode, 'companion_identity_public'));
|
|
146
|
+
if (ref && wrappedKeyBundle && companionIdentityPub && pendingFinish?.ref === ref) {
|
|
147
|
+
pendingFinish.resolve({ companionIdentityPub, wrappedKeyBundle });
|
|
148
|
+
pendingFinish = null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} catch (e) { logger.warn?.({ e }, 'handleIncomingNotification failed'); }
|
|
153
|
+
return false;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const completeLink = async (input) => {
|
|
157
|
+
const accountIdentityKeyPair = requirePrimaryIdentityKeyPair();
|
|
158
|
+
const ep = await ensureEpoch();
|
|
159
|
+
const keyIndex = nextKeyIndex(ep);
|
|
160
|
+
const validIndexes = [...validKeyIndexes(ep), keyIndex].sort((a, b) => a - b);
|
|
161
|
+
const timestampSeconds = Math.floor(Date.now() / 1000);
|
|
162
|
+
const signed = await buildSignedCompanionIdentity({
|
|
163
|
+
accountIdentityKeyPair,
|
|
164
|
+
companionIdentityPublicKey: input.companionIdentityPublicKey,
|
|
165
|
+
advSecretKey: input.advSecretKey,
|
|
166
|
+
rawId: ep.rawId,
|
|
167
|
+
keyIndex,
|
|
168
|
+
timestampSeconds,
|
|
169
|
+
validIndexes
|
|
170
|
+
});
|
|
171
|
+
const iq = buildPairDeviceIq({
|
|
172
|
+
ref: input.ref,
|
|
173
|
+
companionNoisePublicKey: input.companionNoisePublicKey,
|
|
174
|
+
deviceIdentityBytes: signed.deviceIdentityBytes,
|
|
175
|
+
keyIndexListBytes: signed.keyIndexListBytes,
|
|
176
|
+
keyIndexListTimestampSeconds: timestampSeconds,
|
|
177
|
+
clientProps: buildClientPairingProps(),
|
|
178
|
+
pem: config?.companionHost?.includePem ? {
|
|
179
|
+
aesGcmKeyBytes: derivePemKey(input.advSecretKey, input.companionNoisePublicKey),
|
|
180
|
+
ttlSeconds: PEM_TTL_SECONDS
|
|
181
|
+
} : undefined
|
|
182
|
+
});
|
|
183
|
+
const result = await query(iq, PAIR_DEVICE_TIMEOUT_MS);
|
|
184
|
+
// assert no error
|
|
185
|
+
const wrapper = Array.isArray(result?.content) ? result.content[0] : result?.content;
|
|
186
|
+
const deviceNode = findNodeChild(result, 'device') || (wrapper ? findNodeChild(wrapper, 'device') : undefined);
|
|
187
|
+
const deviceJid = deviceNode?.attrs?.jid;
|
|
188
|
+
if (!deviceJid) throw new Error('pair-device result missing <device jid>');
|
|
189
|
+
epoch = {
|
|
190
|
+
rawId: ep.rawId,
|
|
191
|
+
currentKeyIndex: keyIndex,
|
|
192
|
+
companions: [...(ep.companions || []), {
|
|
193
|
+
deviceJid,
|
|
194
|
+
keyIndex,
|
|
195
|
+
companionIdentityPublicKey: input.companionIdentityPublicKey,
|
|
196
|
+
addedAtSeconds: timestampSeconds
|
|
197
|
+
}]
|
|
198
|
+
};
|
|
199
|
+
await persist(epoch);
|
|
200
|
+
emit('companion_host_linked', { deviceJid, keyIndex });
|
|
201
|
+
return { deviceJid, keyIndex };
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const sendHistorySyncBootstrap = async (companionDeviceJid, options = {}) => {
|
|
205
|
+
requirePrimaryIdentityKeyPair();
|
|
206
|
+
const { message, payloadBytes } = await buildHistorySyncBootstrapMessage({
|
|
207
|
+
phoneNumberToLidMappings: options.phoneNumberToLidMappings
|
|
208
|
+
});
|
|
209
|
+
await sock.relayMessage(companionDeviceJid, proto.WebMessageInfo.fromObject({ message: { protocolMessage: message } }), {});
|
|
210
|
+
logger.info?.('companion-host history sync bootstrap sent', { deviceJid: companionDeviceJid, payloadBytes });
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const provisionLinkedCompanion = (deviceJid) => {
|
|
214
|
+
void (async () => {
|
|
215
|
+
let lastError = null;
|
|
216
|
+
for (let attempt = 1; attempt <= 8; attempt++) {
|
|
217
|
+
if (attempt > 1) await new Promise((r) => setTimeout(r, 1500));
|
|
218
|
+
try {
|
|
219
|
+
await sendHistorySyncBootstrap(deviceJid);
|
|
220
|
+
logger.info?.('companion provisioned', { deviceJid, attempt });
|
|
221
|
+
return;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
lastError = error;
|
|
224
|
+
logger.debug?.({ deviceJid, attempt, message: error?.message }, 'companion provisioning attempt failed');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (lastError) emit('companion_host_error', lastError);
|
|
228
|
+
})();
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const mobile = {
|
|
232
|
+
isMobilePrimary,
|
|
233
|
+
async linkCompanion(qr) {
|
|
234
|
+
try {
|
|
235
|
+
const parsed = parseCompanionQr(qr);
|
|
236
|
+
const accountIdentityKeyPair = requirePrimaryIdentityKeyPair();
|
|
237
|
+
const ep = await ensureEpoch();
|
|
238
|
+
// Need build signed identity; advSecret from qr
|
|
239
|
+
const keyIndex = nextKeyIndex(ep);
|
|
240
|
+
// We'll delegate to completeLink-like flow without provisioning
|
|
241
|
+
const validIndexes = [...validKeyIndexes(ep), keyIndex].sort((a, b) => a - b);
|
|
242
|
+
const timestampSeconds = Math.floor(Date.now() / 1000);
|
|
243
|
+
const signed = await buildSignedCompanionIdentity({
|
|
244
|
+
accountIdentityKeyPair,
|
|
245
|
+
companionIdentityPublicKey: parsed.identityPublicKey,
|
|
246
|
+
advSecretKey: parsed.advSecretKey,
|
|
247
|
+
rawId: ep.rawId,
|
|
248
|
+
keyIndex,
|
|
249
|
+
timestampSeconds,
|
|
250
|
+
validIndexes
|
|
251
|
+
});
|
|
252
|
+
const iq = buildPairDeviceIq({
|
|
253
|
+
ref: parsed.ref,
|
|
254
|
+
companionNoisePublicKey: parsed.noisePublicKey,
|
|
255
|
+
deviceIdentityBytes: signed.deviceIdentityBytes,
|
|
256
|
+
keyIndexListBytes: signed.keyIndexListBytes,
|
|
257
|
+
keyIndexListTimestampSeconds: timestampSeconds,
|
|
258
|
+
clientProps: buildClientPairingProps(),
|
|
259
|
+
pem: config?.companionHost?.includePem ? {
|
|
260
|
+
aesGcmKeyBytes: derivePemKey(parsed.advSecretKey, parsed.noisePublicKey),
|
|
261
|
+
ttlSeconds: PEM_TTL_SECONDS
|
|
262
|
+
} : undefined
|
|
263
|
+
});
|
|
264
|
+
const result = await query(iq, PAIR_DEVICE_TIMEOUT_MS);
|
|
265
|
+
const wrapper = Array.isArray(result?.content) ? result.content[0] : result?.content;
|
|
266
|
+
const deviceNode = findNodeChild(result, 'device') || (wrapper ? findNodeChild(wrapper, 'device') : undefined);
|
|
267
|
+
const deviceJid = deviceNode?.attrs?.jid;
|
|
268
|
+
if (!deviceJid) throw new Error('pair-device result missing <device jid>');
|
|
269
|
+
epoch = {
|
|
270
|
+
rawId: ep.rawId,
|
|
271
|
+
currentKeyIndex: keyIndex,
|
|
272
|
+
companions: [...(ep.companions || []), {
|
|
273
|
+
deviceJid,
|
|
274
|
+
keyIndex,
|
|
275
|
+
companionIdentityPublicKey: parsed.identityPublicKey,
|
|
276
|
+
addedAtSeconds: timestampSeconds
|
|
277
|
+
}]
|
|
278
|
+
};
|
|
279
|
+
await persist(epoch);
|
|
280
|
+
emit('companion_host_linked', { deviceJid, keyIndex });
|
|
281
|
+
provisionLinkedCompanion(deviceJid);
|
|
282
|
+
return { deviceJid, keyIndex };
|
|
283
|
+
} catch (error) {
|
|
284
|
+
emit('companion_host_error', error);
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
async linkCompanionByCode(pairingCode) {
|
|
289
|
+
try {
|
|
290
|
+
const accountIdentityKeyPair = requirePrimaryIdentityKeyPair();
|
|
291
|
+
const hello = pendingCompanionHello;
|
|
292
|
+
if (!hello) throw new Error('no pending companion; a companion must request a pairing code for this account first');
|
|
293
|
+
const prepared = await preparePrimaryHello({
|
|
294
|
+
pairingCode: String(pairingCode).replace(/[^0-9A-Za-z]/g, '').toUpperCase(),
|
|
295
|
+
wrappedCompanionEphemeralPub: hello.wrappedCompanionEphemeralPub
|
|
296
|
+
});
|
|
297
|
+
let finishResolve, finishReject;
|
|
298
|
+
const finishPromise = new Promise((resolve, reject) => { finishResolve = resolve; finishReject = reject; });
|
|
299
|
+
pendingFinish = { ref: hello.ref, resolve: finishResolve };
|
|
300
|
+
const timeout = setTimeout(() => {
|
|
301
|
+
if (pendingFinish?.ref === hello.ref) {
|
|
302
|
+
pendingFinish = null;
|
|
303
|
+
finishReject(new Error('link-code companion_finish timed out'));
|
|
304
|
+
}
|
|
305
|
+
}, LINK_CODE_FINISH_TIMEOUT_MS);
|
|
306
|
+
let finish;
|
|
307
|
+
try {
|
|
308
|
+
const helloResult = await query(buildPrimaryHelloIq({
|
|
309
|
+
ref: hello.ref,
|
|
310
|
+
wrappedPrimaryEphemeralPub: prepared.wrappedPrimaryEphemeralPub,
|
|
311
|
+
primaryIdentityPub: toRawPubKey(accountIdentityKeyPair.pubKey)
|
|
312
|
+
}), PRIMARY_HELLO_TIMEOUT_MS);
|
|
313
|
+
finish = await finishPromise;
|
|
314
|
+
} finally {
|
|
315
|
+
clearTimeout(timeout);
|
|
316
|
+
if (pendingFinish?.ref === hello.ref) pendingFinish = null;
|
|
317
|
+
}
|
|
318
|
+
const advSecretKey = await completePrimaryHandshake({
|
|
319
|
+
sharedEphemeral: prepared.sharedEphemeral,
|
|
320
|
+
wrappedKeyBundle: finish.wrappedKeyBundle,
|
|
321
|
+
companionIdentityPub: finish.companionIdentityPub,
|
|
322
|
+
primaryIdentityKeyPair: accountIdentityKeyPair
|
|
323
|
+
});
|
|
324
|
+
pendingCompanionHello = null;
|
|
325
|
+
// reuse linkCompanion completeLink logic by calling linkCompanion? Instead inline pair-device
|
|
326
|
+
const ep = await ensureEpoch();
|
|
327
|
+
const keyIndex = nextKeyIndex(ep);
|
|
328
|
+
const validIndexes = [...validKeyIndexes(ep), keyIndex].sort((a, b) => a - b);
|
|
329
|
+
const timestampSeconds = Math.floor(Date.now() / 1000);
|
|
330
|
+
const signed = await buildSignedCompanionIdentity({
|
|
331
|
+
accountIdentityKeyPair,
|
|
332
|
+
companionIdentityPublicKey: toRawPubKey(finish.companionIdentityPub),
|
|
333
|
+
advSecretKey,
|
|
334
|
+
rawId: ep.rawId,
|
|
335
|
+
keyIndex,
|
|
336
|
+
timestampSeconds,
|
|
337
|
+
validIndexes
|
|
338
|
+
});
|
|
339
|
+
const iq = buildPairDeviceIq({
|
|
340
|
+
ref: hello.ref,
|
|
341
|
+
companionNoisePublicKey: hello.companionServerAuthKeyPub,
|
|
342
|
+
deviceIdentityBytes: signed.deviceIdentityBytes,
|
|
343
|
+
keyIndexListBytes: signed.keyIndexListBytes,
|
|
344
|
+
keyIndexListTimestampSeconds: timestampSeconds,
|
|
345
|
+
clientProps: buildClientPairingProps()
|
|
346
|
+
});
|
|
347
|
+
const result = await query(iq, PAIR_DEVICE_TIMEOUT_MS);
|
|
348
|
+
const wrapper = Array.isArray(result?.content) ? result.content[0] : result?.content;
|
|
349
|
+
const deviceNode = findNodeChild(result, 'device') || (wrapper ? findNodeChild(wrapper, 'device') : undefined);
|
|
350
|
+
const deviceJid = deviceNode?.attrs?.jid;
|
|
351
|
+
if (!deviceJid) throw new Error('pair-device result missing <device jid>');
|
|
352
|
+
epoch = {
|
|
353
|
+
rawId: ep.rawId,
|
|
354
|
+
currentKeyIndex: keyIndex,
|
|
355
|
+
companions: [...(ep.companions || []), {
|
|
356
|
+
deviceJid,
|
|
357
|
+
keyIndex,
|
|
358
|
+
companionIdentityPublicKey: toRawPubKey(finish.companionIdentityPub),
|
|
359
|
+
addedAtSeconds: timestampSeconds
|
|
360
|
+
}]
|
|
361
|
+
};
|
|
362
|
+
await persist(epoch);
|
|
363
|
+
emit('companion_host_linked', { deviceJid, keyIndex });
|
|
364
|
+
provisionLinkedCompanion(deviceJid);
|
|
365
|
+
return { deviceJid, keyIndex };
|
|
366
|
+
} catch (error) {
|
|
367
|
+
emit('companion_host_error', error);
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
async revokeCompanion(deviceJid, reason = 'user_initiated') {
|
|
372
|
+
requirePrimaryIdentityKeyPair();
|
|
373
|
+
const ep = await ensureEpoch();
|
|
374
|
+
if (!(ep.companions || []).some((c) => c.deviceJid === deviceJid)) {
|
|
375
|
+
throw new Error(`companion ${deviceJid} is not tracked by this primary`);
|
|
376
|
+
}
|
|
377
|
+
await query(buildRemoveCompanionDeviceIq({ deviceJid, reason }), REMOVE_DEVICE_TIMEOUT_MS);
|
|
378
|
+
epoch = {
|
|
379
|
+
rawId: ep.rawId,
|
|
380
|
+
currentKeyIndex: ep.currentKeyIndex,
|
|
381
|
+
companions: (ep.companions || []).filter((c) => c.deviceJid !== deviceJid)
|
|
382
|
+
};
|
|
383
|
+
await persist(epoch);
|
|
384
|
+
emit('companion_host_revoked', { deviceJid });
|
|
385
|
+
try { await mobile.publishKeyIndexList(); } catch (e) { logger.warn?.({ e }, 'republish failed'); }
|
|
386
|
+
},
|
|
387
|
+
async revokeAllCompanions(reason = 'user_initiated', options = {}) {
|
|
388
|
+
requirePrimaryIdentityKeyPair();
|
|
389
|
+
const ep = await ensureEpoch();
|
|
390
|
+
await query(buildRemoveAllCompanionDevicesIq({ reason, excludeHostedCompanion: options.excludeHostedCompanion }), REMOVE_DEVICE_TIMEOUT_MS);
|
|
391
|
+
if (!options.excludeHostedCompanion) {
|
|
392
|
+
epoch = { rawId: ep.rawId, currentKeyIndex: ep.currentKeyIndex, companions: [] };
|
|
393
|
+
await persist(epoch);
|
|
394
|
+
}
|
|
395
|
+
emit('companion_host_revoked', { deviceJid: 'all' });
|
|
396
|
+
},
|
|
397
|
+
async listCompanions() {
|
|
398
|
+
const ep = await ensureEpoch();
|
|
399
|
+
return ep.companions || [];
|
|
400
|
+
},
|
|
401
|
+
async reconcileCompanions() {
|
|
402
|
+
return [];
|
|
403
|
+
},
|
|
404
|
+
async publishKeyIndexList() {
|
|
405
|
+
const accountIdentityKeyPair = requirePrimaryIdentityKeyPair();
|
|
406
|
+
const ep = await ensureEpoch();
|
|
407
|
+
const timestampSeconds = Math.floor(Date.now() / 1000);
|
|
408
|
+
const validIndexes = validKeyIndexes(ep);
|
|
409
|
+
const currentIndex = validIndexes[validIndexes.length - 1] || 0;
|
|
410
|
+
const keyIndexListBytes = await buildSignedKeyIndexList({
|
|
411
|
+
accountIdentityKeyPair,
|
|
412
|
+
rawId: ep.rawId,
|
|
413
|
+
currentIndex,
|
|
414
|
+
timestampSeconds,
|
|
415
|
+
validIndexes
|
|
416
|
+
});
|
|
417
|
+
await query(buildKeyIndexListPublishIq({ keyIndexListBytes, timestampSeconds }), KEY_INDEX_TIMEOUT_MS);
|
|
418
|
+
},
|
|
419
|
+
sendHistorySyncBootstrap,
|
|
420
|
+
handleIncomingNotification: handleIncomingNotification
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
return mobile;
|
|
424
|
+
};
|