Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

md-wa

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

md-wa - npm Package Compare versions

Comparing version 4.1.0 to 5.0.0

lib/Utils/auth-utils.d.ts

4

lib/Defaults/index.js

@@ -20,3 +20,3 @@ "use strict";

exports.DEFAULT_CONNECTION_CONFIG = {
version: [2, 2142, 12],
version: [2, 2144, 11],
browser: Utils_1.Browsers.baileys('Chrome'),

@@ -28,3 +28,3 @@ waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',

printQRInTerminal: false,
emitOwnEvents: true
emitOwnEvents: true,
};

@@ -31,0 +31,0 @@ exports.MEDIA_PATH_MAP = {

/// <reference types="ws" />
/// <reference types="node" />
import { SocketConfig, WAPresence, Chat, WAPatchCreate, WAMediaUpload, WAPatchName, ChatModification, Contact } from "../Types";
import { SocketConfig, WAPresence, WAPatchCreate, WAMediaUpload, WAPatchName, ChatModification, Contact } from "../Types";
import { BinaryNode } from "../WABinary";

@@ -22,8 +22,5 @@ import { proto } from '../../WAProto';

updateBlockStatus: (jid: string, action: 'block' | 'unblock') => Promise<void>;
resyncAppState: (collections: WAPatchName[], fromScratch?: boolean, returnSnapshot?: boolean) => Promise<void>;
resyncAppState: (collections: WAPatchName[], returnSnapshot?: boolean) => Promise<void>;
chatModify: (mod: ChatModification, jid: string, lastMessages: Pick<proto.IWebMessageInfo, 'key' | 'messageTimestamp'>[]) => Promise<void>;
processMessage: (message: proto.IWebMessageInfo, chatUpdate: Partial<Chat>) => Promise<void>;
sendMessageAck: ({ tag, attrs }: BinaryNode, extraAttrs: {
[key: string]: string;
}) => Promise<void>;
resyncMainAppState: () => Promise<void>;
assertSession: (jid: string, force: boolean) => Promise<boolean>;

@@ -34,2 +31,3 @@ relayMessage: (jid: string, message: proto.IMessage, { messageId: msgId, additionalAttributes, cachedGroupMetadata }: import("../Types").MessageRelayOptions) => Promise<string>;

refreshMediaConn: (forceGet?: boolean) => Promise<import("../Types").MediaConnInfo>;
waUploadToServer: import("../Types").WAMediaUploadFunction;
fetchPrivacySettings: (force?: boolean) => Promise<{

@@ -41,3 +39,3 @@ [_: string]: string;

groupCreate: (subject: string, participants: string[]) => Promise<import("../Types").GroupMetadata>;
groupLeave: (jid: string) => Promise<void>;
groupLeave: (id: string) => Promise<void>;
groupUpdateSubject: (jid: string, subject: string) => Promise<void>;

@@ -47,2 +45,4 @@ groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<string[]>;

groupInviteCode: (jid: string) => Promise<string>;
groupRevokeInvite: (jid: string) => Promise<string>;
groupAcceptInvite: (code: string) => Promise<string>;
groupToggleEphemeral: (jid: string, ephemeralExpiration: number) => Promise<void>;

@@ -49,0 +49,0 @@ groupSettingUpdate: (jid: string, setting: "announcement" | "not_announcement" | "locked" | "unlocked") => Promise<void>;

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -7,7 +10,9 @@ exports.makeChatsSocket = void 0;

const Utils_1 = require("../Utils");
const messages_recv_1 = require("./messages-recv");
const messages_send_1 = require("./messages-send");
const make_mutex_1 = __importDefault(require("../Utils/make-mutex"));
const makeChatsSocket = (config) => {
const { logger } = config;
const sock = messages_recv_1.makeMessagesRecvSocket(config);
const sock = messages_send_1.makeMessagesSocket(config);
const { ev, ws, authState, generateMessageTag, sendNode, query, fetchPrivacySettings, } = sock;
const mutationMutex = make_mutex_1.default();
const interactiveQuery = async (userNodes, queryNode) => {

@@ -145,3 +150,5 @@ const result = await query({

};
const resyncAppState = async (collections, fromScratch = false, returnSnapshot = false) => {
const resyncAppStateInternal = async (collections, fromScratch = false, returnSnapshot = false) => {
if (fromScratch)
returnSnapshot = true;
const states = {};

@@ -151,3 +158,3 @@ for (const name of collections) {

if (!state)
state = { version: 0, hash: Buffer.alloc(128), mutations: [] };
state = Utils_1.newLTHashState();
states[name] = state;

@@ -178,8 +185,14 @@ logger.info(`resyncing ${name} from v${state.version}`);

});
const decoded = Utils_1.extractSyncdPatches(result); // extract from binary node
const decoded = await Utils_1.extractSyncdPatches(result); // extract from binary node
for (const key in decoded) {
const name = key;
const { patches, snapshot } = decoded[name];
if (snapshot) {
const newState = await Utils_1.decodeSyncdSnapshot(name, snapshot, authState.keys.getAppStateSyncKey);
states[name] = newState;
logger.info(`restored state of ${name} from snapshot to v${newState.version}`);
}
// only process if there are syncd patches
if (decoded[name].length) {
const { newMutations, state: newState } = await Utils_1.decodePatches(name, decoded[name], states[name], authState, true);
if (patches.length) {
const { newMutations, state: newState } = await Utils_1.decodePatches(name, patches, states[name], authState.keys.getAppStateSyncKey, true);
await authState.keys.setAppStateSyncVersion(name, newState);

@@ -190,4 +203,12 @@ logger.info(`synced ${name} to v${newState.version}`);

}
ev.emit('auth-state.update', authState);
};
const resyncAppState = async (collections, returnSnapshot = false) => {
try {
await resyncAppStateInternal(collections, returnSnapshot);
}
catch (error) {
logger.info({ collections, error: error.stack }, 'failed to sync state from version, trying from scratch');
await resyncAppStateInternal(collections, true, true);
}
};
/**

@@ -216,2 +237,3 @@ * fetch the profile picture of a user/group

const sendPresenceUpdate = async (type, toJid) => {
const me = authState.creds.me;
if (type === 'available' || type === 'unavailable') {

@@ -221,3 +243,3 @@ await sendNode({

attrs: {
name: authState.creds.me.name,
name: me.name,
type

@@ -231,3 +253,3 @@ }

attrs: {
from: authState.creds.me.id,
from: me.id,
to: toJid,

@@ -274,2 +296,13 @@ },

};
const resyncMainAppState = async () => {
logger.debug('resyncing main app state');
await (mutationMutex.mutex(() => resyncAppState([
'critical_block',
'critical_unblock_low',
'regular_high',
'regular_low',
'regular'
]))
.catch(err => (logger.warn({ trace: err.stack }, 'failed to sync app state'))));
};
const processSyncActions = (actions) => {

@@ -308,4 +341,7 @@ var _a, _b, _c, _d;

else if (action === null || action === void 0 ? void 0 : action.pushNameSetting) {
authState.creds.me.name = (_d = action === null || action === void 0 ? void 0 : action.pushNameSetting) === null || _d === void 0 ? void 0 : _d.name;
ev.emit('auth-state.update', authState);
const me = {
...authState.creds.me,
name: (_d = action === null || action === void 0 ? void 0 : action.pushNameSetting) === null || _d === void 0 ? void 0 : _d.name
};
ev.emit('creds.update', { me });
}

@@ -334,50 +370,45 @@ else {

const name = patchCreate.type;
try {
await mutationMutex.mutex(async () => {
await resyncAppState([name]);
}
catch (error) {
logger.info({ name, error: error.stack }, 'failed to sync state from version, trying from scratch');
await resyncAppState([name], true);
}
const { patch, state } = await Utils_1.encodeSyncdPatch(patchCreate, authState);
const initial = await authState.keys.getAppStateSyncVersion(name);
// temp: verify it was encoded correctly
const result = await Utils_1.decodePatches(name, [{ ...patch, version: { version: state.version }, }], initial, authState);
const node = {
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
type: 'set',
xmlns: 'w:sync:app:state'
},
content: [
{
tag: 'sync',
attrs: {},
content: [
{
tag: 'collection',
attrs: {
name,
version: (state.version - 1).toString(),
return_snapshot: 'false'
},
content: [
{
tag: 'patch',
attrs: {},
content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
}
]
}
]
}
]
};
await query(node);
await authState.keys.setAppStateSyncVersion(name, state);
ev.emit('auth-state.update', authState);
if (config.emitOwnEvents) {
processSyncActions(result.newMutations);
}
const { patch, state } = await Utils_1.encodeSyncdPatch(patchCreate, authState);
const initial = await authState.keys.getAppStateSyncVersion(name);
// temp: verify it was encoded correctly
const result = await Utils_1.decodePatches(name, [{ ...patch, version: { version: state.version }, }], initial, authState.keys.getAppStateSyncKey);
const node = {
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
type: 'set',
xmlns: 'w:sync:app:state'
},
content: [
{
tag: 'sync',
attrs: {},
content: [
{
tag: 'collection',
attrs: {
name,
version: (state.version - 1).toString(),
return_snapshot: 'false'
},
content: [
{
tag: 'patch',
attrs: {},
content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
}
]
}
]
}
]
};
await query(node);
await authState.keys.setAppStateSyncVersion(name, state);
if (config.emitOwnEvents) {
processSyncActions(result.newMutations);
}
});
};

@@ -406,4 +437,6 @@ const chatModify = (mod, jid, lastMessages) => {

const name = update.attrs.name;
resyncAppState([name], false)
.catch(err => logger.error({ trace: err.stack, node }, `failed to sync state`));
mutationMutex.mutex(async () => {
await resyncAppState([name], false)
.catch(err => logger.error({ trace: err.stack, node }, `failed to sync state`));
});
}

@@ -416,4 +449,2 @@ });

fetchPrivacySettings();
resyncAppState(['critical_block', 'critical_unblock_low'])
.catch(err => logger.info({ trace: err.stack }, 'failed to sync app state'));
}

@@ -434,4 +465,5 @@ });

chatModify,
resyncMainAppState,
};
};
exports.makeChatsSocket = makeChatsSocket;

@@ -8,3 +8,3 @@ /// <reference types="ws" />

groupCreate: (subject: string, participants: string[]) => Promise<GroupMetadata>;
groupLeave: (jid: string) => Promise<void>;
groupLeave: (id: string) => Promise<void>;
groupUpdateSubject: (jid: string, subject: string) => Promise<void>;

@@ -14,2 +14,4 @@ groupParticipantsUpdate: (jid: string, participants: string[], action: ParticipantAction) => Promise<string[]>;

groupInviteCode: (jid: string) => Promise<string>;
groupRevokeInvite: (jid: string) => Promise<string>;
groupAcceptInvite: (code: string) => Promise<string>;
groupToggleEphemeral: (jid: string, ephemeralExpiration: number) => Promise<void>;

@@ -16,0 +18,0 @@ groupSettingUpdate: (jid: string, setting: 'announcement' | 'not_announcement' | 'locked' | 'unlocked') => Promise<void>;

@@ -43,3 +43,3 @@ "use strict";

},
groupLeave: async (jid) => {
groupLeave: async (id) => {
await groupQuery('@g.us', 'set', [

@@ -50,3 +50,3 @@ {

content: [
{ tag: 'group', attrs: { jid } }
{ tag: 'group', attrs: { id } }
]

@@ -95,2 +95,12 @@ }

},
groupRevokeInvite: async (jid) => {
const result = await groupQuery(jid, 'set', [{ tag: 'invite', attrs: {} }]);
const inviteNode = WABinary_1.getBinaryNodeChild(result, 'invite');
return inviteNode.attrs.code;
},
groupAcceptInvite: async (code) => {
const results = await groupQuery('@g.us', 'set', [{ tag: 'invite', attrs: { code } }]);
const result = WABinary_1.getBinaryNodeChild(results, 'group');
return result.attrs.jid;
},
groupToggleEphemeral: async (jid, ephemeralExpiration) => {

@@ -97,0 +107,0 @@ const content = ephemeralExpiration ?

@@ -5,2 +5,6 @@ /// <reference types="ws" />

declare const makeWASocket: (config: Partial<SocketConfig>) => {
processMessage: (message: import("../Types").WAProto.IWebMessageInfo, chatUpdate: Partial<import("../Types").Chat>) => Promise<void>;
sendMessageAck: ({ tag, attrs }: import("..").BinaryNode, extraAttrs: {
[key: string]: string;
}) => Promise<void>;
appPatch: (patchCreate: import("../Types").WAPatchCreate) => Promise<void>;

@@ -21,8 +25,5 @@ sendPresenceUpdate: (type: import("../Types").WAPresence, toJid?: string) => Promise<void>;

updateBlockStatus: (jid: string, action: "block" | "unblock") => Promise<void>;
resyncAppState: (collections: import("../Types").WAPatchName[], fromScratch?: boolean, returnSnapshot?: boolean) => Promise<void>;
resyncAppState: (collections: import("../Types").WAPatchName[], returnSnapshot?: boolean) => Promise<void>;
chatModify: (mod: import("../Types").ChatModification, jid: string, lastMessages: Pick<import("../Types").WAProto.IWebMessageInfo, "key" | "messageTimestamp">[]) => Promise<void>;
processMessage: (message: import("../Types").WAProto.IWebMessageInfo, chatUpdate: Partial<import("../Types").Chat>) => Promise<void>;
sendMessageAck: ({ tag, attrs }: import("..").BinaryNode, extraAttrs: {
[key: string]: string;
}) => Promise<void>;
resyncMainAppState: () => Promise<void>;
assertSession: (jid: string, force: boolean) => Promise<boolean>;

@@ -33,2 +34,3 @@ relayMessage: (jid: string, message: import("../Types").WAProto.IMessage, { messageId: msgId, additionalAttributes, cachedGroupMetadata }: import("../Types").MessageRelayOptions) => Promise<string>;

refreshMediaConn: (forceGet?: boolean) => Promise<import("../Types").MediaConnInfo>;
waUploadToServer: import("../Types").WAMediaUploadFunction;
fetchPrivacySettings: (force?: boolean) => Promise<{

@@ -40,3 +42,3 @@ [_: string]: string;

groupCreate: (subject: string, participants: string[]) => Promise<import("../Types").GroupMetadata>;
groupLeave: (jid: string) => Promise<void>;
groupLeave: (id: string) => Promise<void>;
groupUpdateSubject: (jid: string, subject: string) => Promise<void>;

@@ -46,2 +48,4 @@ groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<string[]>;

groupInviteCode: (jid: string) => Promise<string>;
groupRevokeInvite: (jid: string) => Promise<string>;
groupAcceptInvite: (code: string) => Promise<string>;
groupToggleEphemeral: (jid: string, ephemeralExpiration: number) => Promise<void>;

@@ -48,0 +52,0 @@ groupSettingUpdate: (jid: string, setting: "announcement" | "not_announcement" | "locked" | "unlocked") => Promise<void>;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Defaults_1 = require("../Defaults");
const chats_1 = require("./chats");
const messages_recv_1 = require("./messages-recv");
// export the last socket layer
const makeWASocket = (config) => (chats_1.makeChatsSocket({
const makeWASocket = (config) => (messages_recv_1.makeMessagesRecvSocket({
...Defaults_1.DEFAULT_CONNECTION_CONFIG,

@@ -8,0 +8,0 @@ ...config

@@ -9,2 +9,20 @@ /// <reference types="ws" />

sendMessageAck: ({ tag, attrs }: BinaryNode, extraAttrs: BinaryNodeAttributes) => Promise<void>;
appPatch: (patchCreate: import("../Types").WAPatchCreate) => Promise<void>;
sendPresenceUpdate: (type: import("../Types").WAPresence, toJid?: string) => Promise<void>;
presenceSubscribe: (toJid: string) => Promise<void>;
profilePictureUrl: (jid: string, type?: "image" | "preview") => Promise<string>;
onWhatsApp: (...jids: string[]) => Promise<{
exists: boolean;
jid: string;
}[]>;
fetchBlocklist: () => Promise<void>;
fetchStatus: (jid: string) => Promise<{
status: string;
setAt: Date;
}>;
updateProfilePicture: (jid: string, content: import("../Types").WAMediaUpload) => Promise<void>;
updateBlockStatus: (jid: string, action: "block" | "unblock") => Promise<void>;
resyncAppState: (collections: import("../Types").WAPatchName[], returnSnapshot?: boolean) => Promise<void>;
chatModify: (mod: import("../Types").ChatModification, jid: string, lastMessages: Pick<proto.IWebMessageInfo, "key" | "messageTimestamp">[]) => Promise<void>;
resyncMainAppState: () => Promise<void>;
assertSession: (jid: string, force: boolean) => Promise<boolean>;

@@ -15,2 +33,3 @@ relayMessage: (jid: string, message: proto.IMessage, { messageId: msgId, additionalAttributes, cachedGroupMetadata }: import("../Types").MessageRelayOptions) => Promise<string>;

refreshMediaConn: (forceGet?: boolean) => Promise<import("../Types").MediaConnInfo>;
waUploadToServer: import("../Types").WAMediaUploadFunction;
fetchPrivacySettings: (force?: boolean) => Promise<{

@@ -22,3 +41,3 @@ [_: string]: string;

groupCreate: (subject: string, participants: string[]) => Promise<GroupMetadata>;
groupLeave: (jid: string) => Promise<void>;
groupLeave: (id: string) => Promise<void>;
groupUpdateSubject: (jid: string, subject: string) => Promise<void>;

@@ -28,2 +47,4 @@ groupParticipantsUpdate: (jid: string, participants: string[], action: ParticipantAction) => Promise<string[]>;

groupInviteCode: (jid: string) => Promise<string>;
groupRevokeInvite: (jid: string) => Promise<string>;
groupAcceptInvite: (code: string) => Promise<string>;
groupToggleEphemeral: (jid: string, ephemeralExpiration: number) => Promise<void>;

@@ -30,0 +51,0 @@ groupSettingUpdate: (jid: string, setting: "announcement" | "not_announcement" | "locked" | "unlocked") => Promise<void>;

@@ -9,9 +9,10 @@ "use strict";

const Defaults_1 = require("../Defaults");
const messages_send_1 = require("./messages-send");
const chats_1 = require("./chats");
const groups_1 = require("./groups");
const CALL_TAGS_TO_ACK = ['terminate', 'relaylatency', 'offer'];
const isReadReceipt = (type) => type === 'read' || type === 'read-self';
const makeMessagesRecvSocket = (config) => {
const { logger } = config;
const sock = messages_send_1.makeMessagesSocket(config);
const { ev, authState, ws, assertingPreKeys, sendNode, relayMessage, sendDeliveryReceipt, } = sock;
const sock = chats_1.makeChatsSocket(config);
const { ev, authState, ws, assertingPreKeys, sendNode, relayMessage, sendDeliveryReceipt, resyncMainAppState, } = sock;
const msgRetryMap = config.msgRetryCounterMap || {};

@@ -28,3 +29,3 @@ const sendMessageAck = async ({ tag, attrs }, extraAttrs) => {

if (!!attrs.participant) {
stanza.attrs.participant = WABinary_1.jidNormalizedUser(attrs.participant);
stanza.attrs.participant = attrs.participant;
}

@@ -139,2 +140,3 @@ logger.debug({ recv: attrs, sent: stanza.attrs }, `sent "${tag}" ack`);

case WAProto_1.proto.ProtocolMessage.ProtocolMessageType.APP_STATE_SYNC_KEY_SHARE:
let newAppStateSyncKeyId = '';
for (const { keyData, keyId } of protocolMsg.appStateSyncKeyShare.keys || []) {

@@ -144,5 +146,6 @@ const str = Buffer.from(keyId.keyId).toString('base64');

await authState.keys.setAppStateSyncKey(str, keyData);
authState.creds.myAppStateKeyId = str;
newAppStateSyncKeyId = str;
}
ev.emit('auth-state.update', authState);
ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId });
resyncMainAppState();
break;

@@ -230,3 +233,5 @@ case WAProto_1.proto.ProtocolMessage.ProtocolMessageType.REVOKE:

}
ev.emit('messages.upsert', { messages, type: 'prepend' });
if (messages.length) {
ev.emit('messages.upsert', { messages, type: 'prepend' });
}
break;

@@ -251,2 +256,5 @@ case WAProto_1.proto.HistorySync.HistorySyncHistorySyncType.PUSH_NAME:

result.messageStubParameters = [metadata.subject];
result.key = {
participant: WABinary_1.jidNormalizedUser(metadata.owner)
};
ev.emit('chats.upsert', [{

@@ -275,3 +283,11 @@ id: metadata.id,

result.messageStubType = Types_1.WAMessageStubType[stubType];
result.messageStubParameters = WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid);
const participants = WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid);
if (participants.length === 1 &&
// if recv. "remove" message and sender removed themselves
// mark as left
WABinary_1.areJidsSameUser(participants[0], node.attrs.participant) &&
child.tag === 'remove') {
result.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
}
result.messageStubParameters = participants;
break;

@@ -296,8 +312,2 @@ case 'subject':

switch (child.tag) {
case 'count':
if (child.attrs.value === '0') {
logger.info('recv all pending notifications');
ev.emit('connection.update', { receivedPendingNotifications: true });
}
break;
case 'devices':

@@ -320,5 +330,2 @@ const devices = WABinary_1.getBinaryNodeChildren(child, 'device');

const dec = await Utils_1.decodeMessageStanza(stanza, authState);
if (dec.successes.length) {
ev.emit('auth-state.update', authState);
}
const fullMessages = [];

@@ -336,3 +343,8 @@ const { attrs } = stanza;

};
for (const msg of dec.successes) {
const partialMsg = {
messageTimestamp: dec.timestamp,
pushName: dec.pushname
};
// if there were some successful decryptions
if (dec.successes.length) {
// send message receipt

@@ -371,2 +383,4 @@ let recpAttrs;

logger.debug({ msgId: dec.msgId }, 'sent delivery receipt');
}
for (const msg of dec.successes) {
const message = ((_b = msg.deviceSentMessage) === null || _b === void 0 ? void 0 : _b.message) || msg;

@@ -377,5 +391,3 @@ fullMessages.push({

status: isMe ? WAProto_1.proto.WebMessageInfo.WebMessageInfoStatus.SERVER_ACK : null,
messageTimestamp: dec.timestamp,
pushName: dec.pushname,
participant: dec.participant
...partialMsg
});

@@ -389,3 +401,4 @@ }

messageStubType: Types_1.WAMessageStubType.CIPHERTEXT,
messageStubParameters: [error.message]
messageStubParameters: [error.message],
...partialMsg
});

@@ -399,2 +412,5 @@ }

}
else {
logger.warn({ stanza }, `received node with 0 messages`);
}
});

@@ -415,3 +431,3 @@ ws.on('CB:ack,class:message', async (node) => {

const [child] = WABinary_1.getAllBinaryNodeChildren(node);
if (child.tag === 'terminate' || child.tag === 'relaylatency') {
if (CALL_TAGS_TO_ACK.includes(child.tag)) {
await sendMessageAck(node, { class: 'call', type: child.tag });

@@ -452,3 +468,4 @@ }

participant: node.attrs.participant,
id: node.attrs.id
id: node.attrs.id,
...(msg.key || {})
};

@@ -470,4 +487,3 @@ msg.messageTimestamp = +node.attrs.t;

if (msg.key.fromMe && ((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.name) !== msg.pushName) {
authState.creds.me.name = msg.pushName;
ev.emit('auth-state.update', authState);
ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
}

@@ -474,0 +490,0 @@ }

/// <reference types="ws" />
/// <reference types="node" />
import { SocketConfig, MediaConnInfo, AnyMessageContent, MiscMessageGenerationOptions, MessageRelayOptions } from "../Types";
import { SocketConfig, MediaConnInfo, AnyMessageContent, MiscMessageGenerationOptions, WAMediaUploadFunction, MessageRelayOptions } from "../Types";
import { BinaryNode } from '../WABinary';

@@ -12,2 +12,3 @@ import { proto } from "../../WAProto";

refreshMediaConn: (forceGet?: boolean) => Promise<MediaConnInfo>;
waUploadToServer: WAMediaUploadFunction;
fetchPrivacySettings: (force?: boolean) => Promise<{

@@ -19,3 +20,3 @@ [_: string]: string;

groupCreate: (subject: string, participants: string[]) => Promise<import("../Types").GroupMetadata>;
groupLeave: (jid: string) => Promise<void>;
groupLeave: (id: string) => Promise<void>;
groupUpdateSubject: (jid: string, subject: string) => Promise<void>;

@@ -25,2 +26,4 @@ groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<string[]>;

groupInviteCode: (jid: string) => Promise<string>;
groupRevokeInvite: (jid: string) => Promise<string>;
groupAcceptInvite: (code: string) => Promise<string>;
groupToggleEphemeral: (jid: string, ephemeralExpiration: number) => Promise<void>;

@@ -27,0 +30,0 @@ groupSettingUpdate: (jid: string, setting: "announcement" | "not_announcement" | "locked" | "unlocked") => Promise<void>;

@@ -227,3 +227,3 @@ "use strict";

if (isGroup) {
const { ciphertext, senderKeyDistributionMessageKey } = await Utils_1.encryptSenderKeyMsgSignalProto(destinationJid, encodedMsg, authState);
const { ciphertext, senderKeyDistributionMessageKey } = await Utils_1.encryptSenderKeyMsgSignalProto(destinationJid, encodedMsg, authState.creds.me.id, authState);
let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;

@@ -315,3 +315,2 @@ if (!groupData)

await sendNode(stanza);
ev.emit('auth-state.update', authState);
return msgId;

@@ -364,2 +363,3 @@ };

refreshMediaConn,
waUploadToServer,
fetchPrivacySettings,

@@ -366,0 +366,0 @@ sendMessage: async (jid, content, options = {}) => {

@@ -57,2 +57,3 @@ "use strict";

ws.setMaxListeners(0);
const ev = new events_1.default();
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */

@@ -62,5 +63,12 @@ const ephemeralKeyPair = Utils_1.Curve.generateKeyPair();

const noise = Utils_1.makeNoiseHandler(ephemeralKeyPair);
const authState = initialAuthState || Utils_1.initAuthState();
let authState = initialAuthState;
if (!authState) {
authState = Utils_1.useSingleFileAuthState('./auth-info-multi.json').state;
logger.warn(`
Baileys just created a single file state for your credentials.
This will not be supported soon.
Please pass the credentials in the config itself
`);
}
const { creds } = authState;
const ev = new events_1.default();
let lastDateRecv;

@@ -177,10 +185,14 @@ let epoch = 0;

const assertingPreKeys = async (range, execute) => {
const { newPreKeys, lastPreKeyId, preKeysRange } = Utils_1.generateOrGetPreKeys(authState, range);
creds.serverHasPreKeys = true;
creds.nextPreKeyId = Math.max(lastPreKeyId + 1, creds.nextPreKeyId);
creds.firstUnuploadedPreKeyId = Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1);
const { newPreKeys, lastPreKeyId, preKeysRange } = Utils_1.generateOrGetPreKeys(authState.creds, range);
const update = {
nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId),
firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1)
};
if (!creds.serverHasPreKeys) {
update.serverHasPreKeys = true;
}
await Promise.all(Object.keys(newPreKeys).map(k => authState.keys.setPreKey(+k, newPreKeys[+k])));
const preKeys = await Utils_1.getPreKeys(authState.keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1]);
await execute(preKeys);
ev.emit('auth-state.update', authState);
ev.emit('creds.update', update);
};

@@ -244,2 +256,3 @@ /** generates and uploads a set of pre-keys */

clearInterval(keepAliveReq);
clearInterval(qrTimer);
ws.removeAllListeners('close');

@@ -262,3 +275,3 @@ ws.removeAllListeners('error');

});
ws.removeAllListeners('connection.update');
ev.removeAllListeners('connection.update');
};

@@ -334,20 +347,24 @@ const waitForSocketOpen = async () => {

const logout = async () => {
await sendNode({
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
type: 'set',
id: generateMessageTag(),
xmlns: 'md'
},
content: [
{
tag: 'remove-companion-device',
attrs: {
jid: authState.creds.me.id,
reason: 'user_initiated'
var _a;
const jid = (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id;
if (jid) {
await sendNode({
tag: 'iq',
attrs: {
to: WABinary_1.S_WHATSAPP_NET,
type: 'set',
id: generateMessageTag(),
xmlns: 'md'
},
content: [
{
tag: 'remove-companion-device',
attrs: {
jid: jid,
reason: 'user_initiated'
}
}
}
]
});
]
});
}
end(new boom_1.Boom('Intentional Logout', { statusCode: Types_1.DisconnectReason.loggedOut }));

@@ -417,9 +434,2 @@ };

genPairQR();
const checkConnection = ({ connection }) => {
if (connection === 'open' || connection === 'close') {
clearTimeout(qrTimer);
ev.off('connection.update', checkConnection);
}
};
ev.on('connection.update', checkConnection);
});

@@ -442,5 +452,4 @@ // device paired for the first time

}
Object.assign(creds, updatedCreds);
logger.info({ jid: creds.me.id }, 'registered connection, restart server');
ev.emit('auth-state.update', authState);
logger.info({ jid: updatedCreds.me.id }, 'registered connection, restart server');
ev.emit('creds.update', updatedCreds);
ev.emit('connection.update', { isNewLogin: true, qr: undefined });

@@ -461,4 +470,11 @@ end(new boom_1.Boom('Restart Required', { statusCode: Types_1.DisconnectReason.restartRequired }));

logger.info('opened connection to WA');
clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
ev.emit('connection.update', { connection: 'open' });
});
ws.on('CB:ib,,offline', (node) => {
const child = WABinary_1.getBinaryNodeChild(node, 'offline');
const offlineCount = +child.attrs.count;
logger.info(`got ${offlineCount} offline messages/notifications`);
ev.emit('connection.update', { receivedPendingNotifications: true });
});
ws.on('CB:stream:error', (node) => {

@@ -474,5 +490,10 @@ logger.error({ error: node }, `stream errored out`);

});
ws.on('CB:ib,,downgrade_webclient', () => {
end(new boom_1.Boom('Multi-device beta not joined', { statusCode: Types_1.DisconnectReason.notJoinedBeta }));
});
process.nextTick(() => {
ev.emit('connection.update', { connection: 'connecting', receivedPendingNotifications: false, qr: undefined });
});
// update credentials when required
ev.on('creds.update', update => Object.assign(creds, update));
return {

@@ -479,0 +500,0 @@ ws,

/// <reference types="node" />
import type { Contact } from "./Contact";
import type { proto } from "../../WAProto";
import type { WAPatchName, ChatMutation } from "./Chat";
import type { WAPatchName } from "./Chat";
export declare type KeyPair = {

@@ -25,12 +25,18 @@ public: Uint8Array;

hash: Buffer;
mutations: ChatMutation[];
indexValueMap: {
[indexMacBase64: string]: {
valueMac: Uint8Array | Buffer;
};
};
};
export declare type AuthenticationCreds = {
noiseKey: KeyPair;
signedIdentityKey: KeyPair;
signedPreKey: SignedKeyPair;
registrationId: number;
advSecretKey: string;
export declare type SignalCreds = {
readonly signedIdentityKey: KeyPair;
readonly signedPreKey: SignedKeyPair;
readonly registrationId: number;
};
export declare type AuthenticationCreds = SignalCreds & {
readonly noiseKey: KeyPair;
readonly advSecretKey: string;
me?: Contact;
account?: proto.ADVSignedDeviceIdentity;
account?: proto.IADVSignedDeviceIdentity;
signalIdentities?: SignalIdentity[];

@@ -55,2 +61,6 @@ myAppStateKeyId?: string;

};
export declare type SignalAuthState = {
creds: SignalCreds;
keys: SignalKeyStore;
};
export declare type AuthenticationState = {

@@ -57,0 +67,0 @@ creds: AuthenticationCreds;

@@ -13,3 +13,3 @@ /// <reference types="node" />

import type NodeCache from 'node-cache';
import { AuthenticationState } from './Auth';
import { AuthenticationState, AuthenticationCreds } from './Auth';
import { Chat, PresenceData } from './Chat';

@@ -61,3 +61,4 @@ import { Contact } from './Contact';

badSession = 500,
restartRequired = 410
restartRequired = 410,
notJoinedBeta = 403
}

@@ -97,4 +98,4 @@ export declare type WAInitResponse = {

'connection.update': Partial<ConnectionState>;
/** auth state updated -- some pre keys, or identity keys etc. */
'auth-state.update': AuthenticationState;
/** credentials updated -- some metadata, keys or something */
'creds.update': Partial<AuthenticationCreds>;
/** set chats (history sync), messages are reverse chronologically sorted */

@@ -101,0 +102,0 @@ 'chats.set': {

@@ -28,2 +28,3 @@ "use strict";

DisconnectReason[DisconnectReason["restartRequired"] = 410] = "restartRequired";
DisconnectReason[DisconnectReason["notJoinedBeta"] = 403] = "notJoinedBeta";
})(DisconnectReason = exports.DisconnectReason || (exports.DisconnectReason = {}));
/// <reference types="node" />
import { AuthenticationState, WAPatchCreate, ChatMutation, WAPatchName, LTHashState, ChatModification } from "../Types";
import { AuthenticationState, WAPatchCreate, ChatMutation, WAPatchName, LTHashState, ChatModification, SignalKeyStore } from "../Types";
import { proto } from '../../WAProto';
import { BinaryNode } from '../WABinary';
export declare const mutationKeys: (keydata: Uint8Array) => {
indexKey: Buffer;
valueEncryptionKey: Buffer;
valueMacKey: Buffer;
snapshotMacKey: Buffer;
patchMacKey: Buffer;
};
export declare const generateSnapshotMac: (lthash: Uint8Array, version: number, name: WAPatchName, key: Buffer) => Buffer;
export declare const newLTHashState: () => LTHashState;
export declare const encodeSyncdPatch: ({ type, index, syncAction, apiVersion }: WAPatchCreate, { creds: { myAppStateKeyId }, keys }: AuthenticationState) => Promise<{

@@ -18,17 +11,53 @@ patch: proto.ISyncdPatch;

hash: Buffer;
mutations: ChatMutation[];
indexValueMap: {
[indexMacBase64: string]: {
valueMac: Uint8Array | Buffer;
};
};
};
}>;
export declare const decodeSyncdPatch: (msg: proto.ISyncdPatch, name: WAPatchName, { keys }: AuthenticationState, validateMacs?: boolean) => Promise<{
export declare const decodeSyncdMutations: (msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[], initialState: LTHashState, getAppStateSyncKey: SignalKeyStore['getAppStateSyncKey'], validateMacs: boolean) => Promise<{
hash: Buffer;
indexValueMap: {
[indexMacBase64: string]: {
valueMac: Uint8Array | Buffer;
};
};
mutations: ChatMutation[];
}>;
export declare const extractSyncdPatches: (result: BinaryNode) => {
critical_block: proto.ISyncdPatch[];
critical_unblock_low: proto.ISyncdPatch[];
regular_low: proto.ISyncdPatch[];
regular_high: proto.ISyncdPatch[];
regular: proto.ISyncdPatch[];
};
export declare const decodeSyncdPatch: (msg: proto.ISyncdPatch, name: WAPatchName, initialState: LTHashState, getAppStateSyncKey: SignalKeyStore['getAppStateSyncKey'], validateMacs: boolean) => Promise<{
hash: Buffer;
indexValueMap: {
[indexMacBase64: string]: {
valueMac: Uint8Array | Buffer;
};
};
mutations: ChatMutation[];
}>;
export declare const extractSyncdPatches: (result: BinaryNode) => Promise<{
critical_block: {
patches: proto.ISyncdPatch[];
snapshot?: proto.ISyncdSnapshot;
};
critical_unblock_low: {
patches: proto.ISyncdPatch[];
snapshot?: proto.ISyncdSnapshot;
};
regular_low: {
patches: proto.ISyncdPatch[];
snapshot?: proto.ISyncdSnapshot;
};
regular_high: {
patches: proto.ISyncdPatch[];
snapshot?: proto.ISyncdSnapshot;
};
regular: {
patches: proto.ISyncdPatch[];
snapshot?: proto.ISyncdSnapshot;
};
}>;
export declare const downloadExternalBlob: (blob: proto.IExternalBlobReference) => Promise<Buffer>;
export declare const downloadExternalPatch: (blob: proto.IExternalBlobReference) => Promise<proto.SyncdMutations>;
export declare const decodePatches: (name: WAPatchName, syncds: proto.ISyncdPatch[], initial: LTHashState, auth: AuthenticationState, validateMacs?: boolean) => Promise<{
export declare const decodeSyncdSnapshot: (name: WAPatchName, snapshot: proto.ISyncdSnapshot, getAppStateSyncKey: SignalKeyStore['getAppStateSyncKey'], validateMacs?: boolean) => Promise<LTHashState>;
export declare const decodePatches: (name: WAPatchName, syncds: proto.ISyncdPatch[], initial: LTHashState, getAppStateSyncKey: SignalKeyStore['getAppStateSyncKey'], validateMacs?: boolean) => Promise<{
newMutations: ChatMutation[];

@@ -35,0 +64,0 @@ state: LTHashState;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.chatModificationToAppPatch = exports.decodePatches = exports.downloadExternalPatch = exports.extractSyncdPatches = exports.decodeSyncdPatch = exports.encodeSyncdPatch = exports.generateSnapshotMac = exports.mutationKeys = void 0;
exports.chatModificationToAppPatch = exports.decodePatches = exports.decodeSyncdSnapshot = exports.downloadExternalPatch = exports.downloadExternalBlob = exports.extractSyncdPatches = exports.decodeSyncdPatch = exports.decodeSyncdMutations = exports.encodeSyncdPatch = exports.newLTHashState = void 0;
const boom_1 = require("@hapi/boom");

@@ -21,3 +21,2 @@ const crypto_1 = require("./crypto");

};
exports.mutationKeys = mutationKeys;
const generateMac = (operation, data, keyId, key) => {

@@ -49,23 +48,35 @@ const getKeyData = () => {

};
const computeLtHash = (initial, macs, getPrevSetValueMac) => {
const makeLtHashGenerator = ({ indexValueMap, hash }) => {
indexValueMap = { ...indexValueMap };
const addBuffs = [];
const subBuffs = [];
for (let i = 0; i < macs.length; i++) {
const { indexMac, valueMac, operation } = macs[i];
const subBuff = getPrevSetValueMac(indexMac, i);
if (operation === WAProto_1.proto.SyncdMutation.SyncdMutationSyncdOperation.REMOVE) {
if (!subBuff) {
throw new boom_1.Boom('tried remove, but no buffer', { statusCode: 500 });
return {
mix: ({ indexMac, valueMac, operation }) => {
const indexMacBase64 = Buffer.from(indexMac).toString('base64');
const prevOp = indexValueMap[indexMacBase64];
if (operation === WAProto_1.proto.SyncdMutation.SyncdMutationSyncdOperation.REMOVE) {
if (!prevOp) {
throw new boom_1.Boom('tried remove, but no previous op', { data: { indexMac, valueMac } });
}
// remove from index value mac, since this mutation is erased
delete indexValueMap[indexMacBase64];
}
else {
addBuffs.push(new Uint8Array(valueMac).buffer);
// add this index into the history map
indexValueMap[indexMacBase64] = { valueMac };
}
if (prevOp) {
subBuffs.push(new Uint8Array(prevOp.valueMac).buffer);
}
},
finish: () => {
const result = lt_hash_1.LT_HASH_ANTI_TAMPERING.subtractThenAdd(new Uint8Array(hash).buffer, addBuffs, subBuffs);
const buffer = Buffer.from(result);
return {
hash: buffer,
indexValueMap
};
}
else {
addBuffs.push(new Uint8Array(valueMac).buffer);
}
if (subBuff) {
subBuffs.push(new Uint8Array(subBuff).buffer);
}
}
const result = lt_hash_1.LT_HASH_ANTI_TAMPERING.subtractThenAdd(new Uint8Array(initial).buffer, addBuffs, subBuffs);
const buff = Buffer.from(result);
return buff;
};
};

@@ -80,3 +91,2 @@ const generateSnapshotMac = (lthash, version, name, key) => {

};
exports.generateSnapshotMac = generateSnapshotMac;
const generatePatchMac = (snapshotMac, valueMacs, version, type, key) => {

@@ -91,2 +101,4 @@ const total = Buffer.concat([

};
const newLTHashState = () => ({ version: 0, hash: Buffer.alloc(128), indexValueMap: {} });
exports.newLTHashState = newLTHashState;
const encodeSyncdPatch = async ({ type, index, syncAction, apiVersion }, { creds: { myAppStateKeyId }, keys }) => {

@@ -107,9 +119,12 @@ const key = !!myAppStateKeyId ? await keys.getAppStateSyncKey(myAppStateKeyId) : undefined;

}).finish();
const keyValue = exports.mutationKeys(key.keyData);
const keyValue = mutationKeys(key.keyData);
const encValue = crypto_1.aesEncrypt(encoded, keyValue.valueEncryptionKey);
const valueMac = generateMac(operation, encValue, encKeyId, keyValue.valueMacKey);
const indexMac = crypto_1.hmacSign(indexBuffer, keyValue.indexKey);
state.hash = computeLtHash(state.hash, [{ indexMac, valueMac, operation }], (index) => { var _a; return (_a = [...state.mutations].reverse().find(m => Buffer.compare(m.indexMac, index) === 0)) === null || _a === void 0 ? void 0 : _a.valueMac; });
// update LT hash
const generator = makeLtHashGenerator(state);
generator.mix({ indexMac, valueMac, operation });
Object.assign(state, generator.finish());
state.version += 1;
const snapshotMac = exports.generateSnapshotMac(state.hash, state.version, type, keyValue.snapshotMacKey);
const snapshotMac = generateSnapshotMac(state.hash, state.version, type, keyValue.snapshotMacKey);
const patch = {

@@ -134,16 +149,8 @@ patchMac: generatePatchMac(snapshotMac, [valueMac], state.version, type, keyValue.patchMacKey),

};
state.mutations = [
...state.mutations,
{
action: syncAction,
index,
valueMac,
indexMac,
operation
}
];
const base64Index = indexMac.toString('base64');
state.indexValueMap[base64Index] = { valueMac };
return { patch, state };
};
exports.encodeSyncdPatch = encodeSyncdPatch;
const decodeSyncdPatch = async (msg, name, { keys }, validateMacs = true) => {
const decodeSyncdMutations = async (msgMutations, initialState, getAppStateSyncKey, validateMacs) => {
const keyCache = {};

@@ -154,7 +161,7 @@ const getKey = async (keyId) => {

if (!key) {
const keyEnc = await keys.getAppStateSyncKey(base64Key);
const keyEnc = await getAppStateSyncKey(base64Key);
if (!keyEnc) {
throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 500, data: msg });
throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 500, data: { msgMutations } });
}
const result = exports.mutationKeys(keyEnc.keyData);
const result = mutationKeys(keyEnc.keyData);
keyCache[base64Key] = result;

@@ -165,15 +172,12 @@ key = result;

};
const ltGenerator = makeLtHashGenerator(initialState);
const mutations = [];
if (validateMacs) {
const mainKey = await getKey(msg.keyId.id);
const mutationmacs = msg.mutations.map(mutation => mutation.record.value.blob.slice(-32));
const patchMac = generatePatchMac(msg.snapshotMac, mutationmacs, generics_1.toNumber(msg.version.version), name, mainKey.patchMacKey);
if (Buffer.compare(patchMac, msg.patchMac) !== 0) {
throw new boom_1.Boom('Invalid patch mac');
}
}
// indexKey used to HMAC sign record.index.blob
// valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32]
// the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId
for (const { operation, record } of msg.mutations) {
for (const msgMutation of msgMutations) {
// if it's a syncdmutation, get the operation property
// otherwise, if it's only a record -- it'll be a SET mutation
const operation = 'operation' in msgMutation ? msgMutation.operation : WAProto_1.proto.SyncdMutation.SyncdMutationSyncdOperation.SET;
const record = ('record' in msgMutation && !!msgMutation.record) ? msgMutation.record : msgMutation;
const key = await getKey(record.keyId.id);

@@ -198,3 +202,3 @@ const content = Buffer.from(record.value.blob);

const indexStr = Buffer.from(syncAction.index).toString();
mutations.push({
const mutation = {
action: syncAction.value,

@@ -205,18 +209,48 @@ index: JSON.parse(indexStr),

operation: operation
});
};
mutations.push(mutation);
ltGenerator.mix(mutation);
}
return { mutations };
return { mutations, ...ltGenerator.finish() };
};
exports.decodeSyncdMutations = decodeSyncdMutations;
const decodeSyncdPatch = async (msg, name, initialState, getAppStateSyncKey, validateMacs) => {
if (validateMacs) {
const base64Key = Buffer.from(msg.keyId.id).toString('base64');
const mainKeyObj = await getAppStateSyncKey(base64Key);
const mainKey = mutationKeys(mainKeyObj.keyData);
const mutationmacs = msg.mutations.map(mutation => mutation.record.value.blob.slice(-32));
const patchMac = generatePatchMac(msg.snapshotMac, mutationmacs, generics_1.toNumber(msg.version.version), name, mainKey.patchMacKey);
if (Buffer.compare(patchMac, msg.patchMac) !== 0) {
throw new boom_1.Boom('Invalid patch mac');
}
}
const result = await exports.decodeSyncdMutations(msg.mutations, initialState, getAppStateSyncKey, validateMacs);
return result;
};
exports.decodeSyncdPatch = decodeSyncdPatch;
const extractSyncdPatches = (result) => {
const extractSyncdPatches = async (result) => {
const syncNode = WABinary_1.getBinaryNodeChild(result, 'sync');
const collectionNodes = WABinary_1.getBinaryNodeChildren(syncNode, 'collection');
const final = {};
for (const collectionNode of collectionNodes) {
await Promise.all(collectionNodes.map(async (collectionNode) => {
const patchesNode = WABinary_1.getBinaryNodeChild(collectionNode, 'patches');
const patches = WABinary_1.getBinaryNodeChildren(patchesNode || collectionNode, 'patch');
const snapshotNode = WABinary_1.getBinaryNodeChild(collectionNode, 'snapshot');
const syncds = [];
const name = collectionNode.attrs.name;
let snapshot = undefined;
if (snapshotNode && !!snapshotNode.content) {
if (!Buffer.isBuffer(snapshotNode)) {
snapshotNode.content = Buffer.from(Object.values(snapshotNode.content));
}
const blobRef = WAProto_1.proto.ExternalBlobReference.decode(snapshotNode.content);
const data = await exports.downloadExternalBlob(blobRef);
snapshot = WAProto_1.proto.SyncdSnapshot.decode(data);
}
for (let { content } of patches) {
if (content) {
if (!Buffer.isBuffer(content)) {
content = Buffer.from(Object.values(content));
}
const syncd = WAProto_1.proto.SyncdPatch.decode(content);

@@ -229,8 +263,8 @@ if (!syncd.version) {

}
final[name] = syncds;
}
final[name] = { patches: syncds, snapshot };
}));
return final;
};
exports.extractSyncdPatches = extractSyncdPatches;
const downloadExternalPatch = async (blob) => {
const downloadExternalBlob = async (blob) => {
const stream = await messages_media_1.downloadContentFromMessage(blob, 'md-app-state');

@@ -241,2 +275,7 @@ let buffer = Buffer.from([]);

}
return buffer;
};
exports.downloadExternalBlob = downloadExternalBlob;
const downloadExternalPatch = async (blob) => {
const buffer = await exports.downloadExternalBlob(blob);
const syncData = WAProto_1.proto.SyncdMutations.decode(buffer);

@@ -246,61 +285,60 @@ return syncData;

exports.downloadExternalPatch = downloadExternalPatch;
const decodePatches = async (name, syncds, initial, auth, validateMacs = true) => {
const decodeSyncdSnapshot = async (name, snapshot, getAppStateSyncKey, validateMacs = true) => {
const newState = exports.newLTHashState();
newState.version = generics_1.toNumber(snapshot.version.version);
const records = snapshot.records;
const ltGenerator = makeLtHashGenerator(newState);
for (const { index, value } of records) {
const valueMac = value.blob.slice(-32);
ltGenerator.mix({ indexMac: index.blob, valueMac, operation: 0 });
}
Object.assign(newState, ltGenerator.finish());
if (validateMacs) {
const base64Key = Buffer.from(snapshot.keyId.id).toString('base64');
const keyEnc = await getAppStateSyncKey(base64Key);
if (!keyEnc) {
throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 500 });
}
const result = mutationKeys(keyEnc.keyData);
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey);
if (Buffer.compare(snapshot.mac, computedSnapshotMac) !== 0) {
throw new boom_1.Boom(`failed to verify LTHash at ${newState.version} of ${name} from snapshot`, { statusCode: 500 });
}
}
return newState;
};
exports.decodeSyncdSnapshot = decodeSyncdSnapshot;
const decodePatches = async (name, syncds, initial, getAppStateSyncKey, validateMacs = true) => {
const successfulMutations = [];
let current = initial.hash;
let currentVersion = initial.version;
const newState = {
...initial,
indexValueMap: { ...initial.indexValueMap }
};
for (const syncd of syncds) {
const { mutations, version, keyId, snapshotMac, externalMutations } = syncd;
if (externalMutations) {
const ref = await exports.downloadExternalPatch(externalMutations);
mutations.push(...ref.mutations);
const { version, keyId, snapshotMac } = syncd;
if (syncd.externalMutations) {
const ref = await exports.downloadExternalPatch(syncd.externalMutations);
syncd.mutations.push(...ref.mutations);
}
const macs = mutations.map(m => ({
operation: m.operation,
indexMac: m.record.index.blob,
valueMac: m.record.value.blob.slice(-32)
}));
currentVersion = generics_1.toNumber(version.version);
current = computeLtHash(current, macs, (index, maxIndex) => {
let value;
for (const item of initial.mutations) {
if (Buffer.compare(item.indexMac, index) === 0) {
value = item.valueMac;
}
}
for (const { version, mutations } of syncds) {
const versionNum = generics_1.toNumber(version.version);
const mutationIdx = mutations.findIndex(m => {
return Buffer.compare(m.record.index.blob, index) === 0;
});
if (mutationIdx >= 0 && (versionNum < currentVersion || mutationIdx < maxIndex)) {
value = mutations[mutationIdx].record.value.blob.slice(-32);
}
if (versionNum >= currentVersion) {
break;
}
}
return value;
});
newState.version = generics_1.toNumber(version.version);
const decodeResult = await exports.decodeSyncdPatch(syncd, name, newState, getAppStateSyncKey, validateMacs);
newState.hash = decodeResult.hash;
newState.indexValueMap = decodeResult.indexValueMap;
successfulMutations.push(...decodeResult.mutations);
if (validateMacs) {
const base64Key = Buffer.from(keyId.id).toString('base64');
const keyEnc = await auth.keys.getAppStateSyncKey(base64Key);
const keyEnc = await getAppStateSyncKey(base64Key);
if (!keyEnc) {
throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 500 });
throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`);
}
const result = exports.mutationKeys(keyEnc.keyData);
const computedSnapshotMac = exports.generateSnapshotMac(current, currentVersion, name, result.snapshotMacKey);
const result = mutationKeys(keyEnc.keyData);
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey);
if (Buffer.compare(snapshotMac, computedSnapshotMac) !== 0) {
throw new boom_1.Boom(`failed to verify LTHash at ${currentVersion} of ${name}`, { statusCode: 500 });
throw new boom_1.Boom(`failed to verify LTHash at ${newState.version} of ${name}`);
}
}
const decodeResult = await exports.decodeSyncdPatch(syncd, name, auth, validateMacs);
successfulMutations.push(...decodeResult.mutations);
}
return {
newMutations: successfulMutations,
state: {
hash: current,
version: currentVersion,
mutations: [...initial.mutations, ...successfulMutations]
}
state: newState
};

@@ -307,0 +345,0 @@ };

@@ -12,1 +12,2 @@ export * from './decode-wa-message';

export * from './lt-hash';
export * from './auth-utils';

@@ -24,1 +24,2 @@ "use strict";

__exportStar(require("./lt-hash"), exports);
__exportStar(require("./auth-utils"), exports);

@@ -90,3 +90,3 @@ "use strict";

delete uploadData.media;
const content = {
const obj = Types_1.WAProto.Message.fromObject({
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({

@@ -100,6 +100,5 @@ url: mediaUrl,

})
};
const obj = Types_1.WAProto.Message.fromObject(content);
});
if (cacheableKey) {
options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj));
options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj).finish());
}

@@ -106,0 +105,0 @@ return obj;

/// <reference types="node" />
import { SignalIdentity, SignalKeyStore, SignedKeyPair, KeyPair, AuthenticationState } from "../Types/Auth";
import { SignalIdentity, SignalKeyStore, SignedKeyPair, KeyPair, SignalAuthState, AuthenticationCreds } from "../Types/Auth";
import { BinaryNode, JidWithDevice } from "../WABinary";

@@ -12,3 +12,3 @@ import { proto } from "../../WAProto";

}>;
export declare const generateOrGetPreKeys: ({ creds }: AuthenticationState, range: number) => {
export declare const generateOrGetPreKeys: (creds: AuthenticationCreds, range: number) => {
newPreKeys: {

@@ -22,3 +22,3 @@ [id: number]: KeyPair;

export declare const xmppPreKey: (pair: KeyPair, id: number) => BinaryNode;
export declare const signalStorage: ({ creds, keys }: AuthenticationState) => {
export declare const signalStorage: ({ creds, keys }: SignalAuthState) => {
loadSession: (id: any) => Promise<any>;

@@ -44,14 +44,14 @@ storeSession: (id: any, session: any) => Promise<void>;

};
export declare const decryptGroupSignalProto: (group: string, user: string, msg: Buffer | Uint8Array, auth: AuthenticationState) => any;
export declare const processSenderKeyMessage: (authorJid: string, item: proto.ISenderKeyDistributionMessage, auth: AuthenticationState) => Promise<void>;
export declare const decryptSignalProto: (user: string, type: 'pkmsg' | 'msg', msg: Buffer | Uint8Array, auth: AuthenticationState) => Promise<Buffer>;
export declare const encryptSignalProto: (user: string, buffer: Buffer, auth: AuthenticationState) => Promise<{
export declare const decryptGroupSignalProto: (group: string, user: string, msg: Buffer | Uint8Array, auth: SignalAuthState) => any;
export declare const processSenderKeyMessage: (authorJid: string, item: proto.ISenderKeyDistributionMessage, auth: SignalAuthState) => Promise<void>;
export declare const decryptSignalProto: (user: string, type: 'pkmsg' | 'msg', msg: Buffer | Uint8Array, auth: SignalAuthState) => Promise<Buffer>;
export declare const encryptSignalProto: (user: string, buffer: Buffer, auth: SignalAuthState) => Promise<{
type: string;
ciphertext: Buffer;
}>;
export declare const encryptSenderKeyMsgSignalProto: (group: string, data: Uint8Array | Buffer, auth: AuthenticationState) => Promise<{
export declare const encryptSenderKeyMsgSignalProto: (group: string, data: Uint8Array | Buffer, meId: string, auth: SignalAuthState) => Promise<{
ciphertext: Uint8Array;
senderKeyDistributionMessageKey: Buffer;
}>;
export declare const parseAndInjectE2ESession: (node: BinaryNode, auth: AuthenticationState) => Promise<void>;
export declare const parseAndInjectE2ESession: (node: BinaryNode, auth: SignalAuthState) => Promise<void>;
export declare const extractDeviceJids: (result: BinaryNode, myDeviceId: number, excludeZeroDevices: boolean) => JidWithDevice[];

@@ -61,3 +61,3 @@ "use strict";

exports.getPreKeys = getPreKeys;
const generateOrGetPreKeys = ({ creds }, range) => {
const generateOrGetPreKeys = (creds, range) => {
const avaliable = creds.nextPreKeyId - creds.firstUnuploadedPreKeyId;

@@ -189,5 +189,5 @@ const remaining = range - avaliable;

exports.encryptSignalProto = encryptSignalProto;
const encryptSenderKeyMsgSignalProto = async (group, data, auth) => {
const encryptSenderKeyMsgSignalProto = async (group, data, meId, auth) => {
const storage = exports.signalStorage(auth);
const senderName = exports.jidToSignalSenderKeyName(group, auth.creds.me.id);
const senderName = exports.jidToSignalSenderKeyName(group, meId);
const builder = new WASignalGroup_1.GroupSessionBuilder(storage);

@@ -194,0 +194,0 @@ const senderKey = await auth.keys.getSenderKey(senderName);

@@ -1,24 +0,5 @@

import { proto } from '../../WAProto';
import type { AuthenticationState, SocketConfig, SignalKeyStore, AuthenticationCreds, KeyPair, LTHashState } from "../Types";
import type { SocketConfig, AuthenticationCreds, SignalCreds } from "../Types";
import { BinaryNode } from '../WABinary';
export declare const generateLoginNode: (userJid: string, config: Pick<SocketConfig, 'version' | 'browser'>) => Uint8Array;
export declare const generateRegistrationNode: ({ registrationId, signedPreKey, signedIdentityKey }: Pick<AuthenticationCreds, 'registrationId' | 'signedPreKey' | 'signedIdentityKey'>, config: Pick<SocketConfig, 'version' | 'browser'>) => Uint8Array;
export declare const initInMemoryKeyStore: ({ preKeys, sessions, senderKeys, appStateSyncKeys, appStateVersions }?: {
preKeys?: {
[k: number]: KeyPair;
};
sessions?: {
[k: string]: any;
};
senderKeys?: {
[k: string]: any;
};
appStateSyncKeys?: {
[k: string]: proto.IAppStateSyncKeyData;
};
appStateVersions?: {
[k: string]: LTHashState;
};
}) => SignalKeyStore;
export declare const initAuthState: () => AuthenticationState;
export declare const generateRegistrationNode: ({ registrationId, signedPreKey, signedIdentityKey }: SignalCreds, config: Pick<SocketConfig, 'version' | 'browser'>) => Uint8Array;
export declare const configureSuccessfulPairing: (stanza: BinaryNode, { advSecretKey, signedIdentityKey, signalIdentities }: Pick<AuthenticationCreds, 'advSecretKey' | 'signedIdentityKey' | 'signalIdentities'>) => {

@@ -25,0 +6,0 @@ creds: Partial<AuthenticationCreds>;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.configureSuccessfulPairing = exports.initAuthState = exports.initInMemoryKeyStore = exports.generateRegistrationNode = exports.generateLoginNode = void 0;
exports.configureSuccessfulPairing = exports.generateRegistrationNode = exports.generateLoginNode = void 0;
const boom_1 = require("@hapi/boom");
const crypto_1 = require("crypto");
const WAProto_1 = require("../../WAProto");
const crypto_2 = require("./crypto");
const crypto_1 = require("./crypto");
const generics_1 = require("./generics");

@@ -78,81 +77,2 @@ const WABinary_1 = require("../WABinary");

exports.generateRegistrationNode = generateRegistrationNode;
const initInMemoryKeyStore = ({ preKeys, sessions, senderKeys, appStateSyncKeys, appStateVersions } = {}) => {
preKeys = preKeys || {};
sessions = sessions || {};
senderKeys = senderKeys || {};
appStateSyncKeys = appStateSyncKeys || {};
appStateVersions = appStateVersions || {};
return {
preKeys,
sessions,
senderKeys,
appStateSyncKeys,
appStateVersions,
getPreKey: keyId => preKeys[keyId],
setPreKey: (keyId, pair) => {
if (pair)
preKeys[keyId] = pair;
else
delete preKeys[keyId];
},
getSession: id => sessions[id],
setSession: (id, item) => {
if (item)
sessions[id] = item;
else
delete sessions[id];
},
getSenderKey: id => {
return senderKeys[id];
},
setSenderKey: (id, item) => {
if (item)
senderKeys[id] = item;
else
delete senderKeys[id];
},
getAppStateSyncKey: id => {
const obj = appStateSyncKeys[id];
if (obj) {
return WAProto_1.proto.AppStateSyncKeyData.fromObject(obj);
}
},
setAppStateSyncKey: (id, item) => {
if (item)
appStateSyncKeys[id] = item;
else
delete appStateSyncKeys[id];
},
getAppStateSyncVersion: id => {
const obj = appStateVersions[id];
if (obj) {
return obj;
}
},
setAppStateSyncVersion: (id, item) => {
if (item)
appStateVersions[id] = item;
else
delete appStateVersions[id];
}
};
};
exports.initInMemoryKeyStore = initInMemoryKeyStore;
const initAuthState = () => {
const identityKey = crypto_2.Curve.generateKeyPair();
return {
creds: {
noiseKey: crypto_2.Curve.generateKeyPair(),
signedIdentityKey: identityKey,
signedPreKey: crypto_2.signedKeyPair(identityKey, 1),
registrationId: generics_1.generateRegistrationId(),
advSecretKey: crypto_1.randomBytes(32).toString('base64'),
nextPreKeyId: 1,
firstUnuploadedPreKeyId: 1,
serverHasPreKeys: false
},
keys: exports.initInMemoryKeyStore()
};
};
exports.initAuthState = initAuthState;
const configureSuccessfulPairing = (stanza, { advSecretKey, signedIdentityKey, signalIdentities }) => {

@@ -168,3 +88,3 @@ var _a, _b, _c, _d, _e;

const { details, hmac } = WAProto_1.proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentity);
const advSign = crypto_2.hmacSign(details, Buffer.from(advSecretKey, 'base64'));
const advSign = crypto_1.hmacSign(details, Buffer.from(advSecretKey, 'base64'));
if (Buffer.compare(hmac, advSign) !== 0) {

@@ -176,7 +96,7 @@ throw new boom_1.Boom('Invalid pairing');

const accountMsg = WABinary_1.Binary.build(new Uint8Array([6, 0]), account.details, signedIdentityKey.public).readByteArray();
if (!crypto_2.Curve.verify(accountSignatureKey, accountMsg, accountSignature)) {
if (!crypto_1.Curve.verify(accountSignatureKey, accountMsg, accountSignature)) {
throw new boom_1.Boom('Failed to verify account signature');
}
const deviceMsg = WABinary_1.Binary.build(new Uint8Array([6, 1]), account.details, signedIdentityKey.public, account.accountSignatureKey).readByteArray();
account.deviceSignature = crypto_2.Curve.sign(signedIdentityKey.private, deviceMsg);
account.deviceSignature = crypto_1.Curve.sign(signedIdentityKey.private, deviceMsg);
const identity = signal_1.createSignalIdentity(jid, accountSignatureKey);

@@ -183,0 +103,0 @@ const keyIndex = WAProto_1.proto.ADVDeviceIdentity.decode(account.details).keyIndex;

{
"name": "md-wa",
"version": "4.1.0",
"version": "5.0.0",
"description": "WhatsApp Multi-Device API",

@@ -5,0 +5,0 @@ "homepage": "https://github.com/adiwajshing/Baileys",

@@ -120,46 +120,19 @@ # Baileys MD - Typescript/Javascript WhatsApp Web API

So, you can save the credentials to log back in via:
So, you can load the credentials to log back in:
``` ts
import makeWASocket, { BufferJSON } from '@adiwajshing/baileys-md'
import makeWASocket, { BufferJSON, useSingleFileAuthState } from '@adiwajshing/baileys-md'
import * as fs from 'fs'
// will initialize a default in-memory auth session
const conn = makeSocket()
// utility function to help save the auth state in a single file
// it's utility ends at demos -- as re-writing a large file over and over again is very inefficient
const { state, saveState } = useSingleFileAuthState('./auth_info_multi.json')
// will use the given state to connect
// so if valid credentials are available -- it'll connect without QR
const conn = makeSocket({ auth: state })
// this will be called as soon as the credentials are updated
conn.ev.on ('auth-state.update', () => {
// save credentials whenever updated
console.log (`credentials updated!`)
const authInfo = conn.authState // get all the auth info we need to restore this session
// save this info to a file
fs.writeFileSync(
'./auth_info.json',
JSON.stringify(authInfo, BufferJSON.replacer, 2)
)
})
conn.ev.on ('creds.update', saveState)
```
Then, to restore a session:
``` ts
import makeWASocket, { BufferJSON, initInMemoryKeyStore } from '@adiwajshing/baileys-md'
import * as fs from 'fs'
**Note**: When a message is received/sent, due to signal sessions needing updating, the auth keys (`authState.keys`) will update. Whenever that happens, you must save the updated keys. Not doing so will prevent your messages from reaching the recipient & other unexpected consequences. The `useSingleFileAuthState` function automatically takes care of that, but for any other serious implementation -- you will need to be very careful with the key state management.
const authJSON = JSON.parse(
fs.readFileSync(
'./auth_info.json',
{ encoding: 'utf-8' }
),
BufferJSON.reviver
)
const auth = {
creds: authJSON.creds,
// stores pre-keys, session & other keys in a JSON object
// we deserialize it here
keys: initInMemoryKeyStore(authJSON.keys)
}
const conn = makeWASocket(auth)
// yay will connect without scanning QR
```
**Note**: Upon every successive connection, the auth state can update part of the stored credentials. It will also update when a message is received/sent due to signal sessions needing updating. Whenever that happens, the `auth-state.update` event is fired uploaded, and you must update your saved credentials upon receiving the event. Not doing so will prevent your messages from reaching the recipient & other unexpected consequences.
## Listening to Connection Updates

@@ -200,4 +173,4 @@

'connection.update': Partial<ConnectionState>
/** auth state updated -- some pre keys, or identity keys etc. */
'auth-state.update': AuthenticationState
/** auth credentials updated -- some pre key state, device ID etc. */
'creds.update': Partial<AuthenticationCreds>
/** set chats (history sync), messages are reverse chronologically sorted */

@@ -605,3 +578,3 @@ 'chats.set': { chats: Chat[], messages: WAMessage[] }

const metadata = await conn.groupMetadata("abcd-xyz@g.us")
console.log(json.id + ", title: " + json.subject + ", description: " + json.desc)
console.log(metadata.id + ", title: " + metadata.subject + ", description: " + metadata.desc)
```

@@ -608,0 +581,0 @@ - To join the group using the invitation code (not supported yet)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc