Path: blob/master/node_modules/@adiwajshing/baileys/lib/Socket/messages-send.js
1129 views
"use strict";1var __importDefault = (this && this.__importDefault) || function (mod) {2return (mod && mod.__esModule) ? mod : { "default": mod };3};4Object.defineProperty(exports, "__esModule", { value: true });5exports.makeMessagesSocket = void 0;6const boom_1 = require("@hapi/boom");7const node_cache_1 = __importDefault(require("node-cache"));8const WAProto_1 = require("../../WAProto");9const Defaults_1 = require("../Defaults");10const Utils_1 = require("../Utils");11const link_preview_1 = require("../Utils/link-preview");12const WABinary_1 = require("../WABinary");13const groups_1 = require("./groups");14const makeMessagesSocket = (config) => {15const { logger, linkPreviewImageThumbnailWidth } = config;16const sock = (0, groups_1.makeGroupsSocket)(config);17const { ev, authState, upsertMessage, query, fetchPrivacySettings, generateMessageTag, sendNode, groupMetadata, groupToggleEphemeral } = sock;18const userDevicesCache = config.userDevicesCache || new node_cache_1.default({19stdTTL: 300,20useClones: false21});22let mediaConn;23const refreshMediaConn = async (forceGet = false) => {24const media = await mediaConn;25if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {26mediaConn = (async () => {27const result = await query({28tag: 'iq',29attrs: {30type: 'set',31xmlns: 'w:m',32to: WABinary_1.S_WHATSAPP_NET,33},34content: [{ tag: 'media_conn', attrs: {} }]35});36const mediaConnNode = (0, WABinary_1.getBinaryNodeChild)(result, 'media_conn');37const node = {38hosts: (0, WABinary_1.getBinaryNodeChildren)(mediaConnNode, 'host').map(item => item.attrs),39auth: mediaConnNode.attrs.auth,40ttl: +mediaConnNode.attrs.ttl,41fetchDate: new Date()42};43logger.debug('fetched media conn');44return node;45})();46}47return mediaConn;48};49/**50* generic send receipt function51* used for receipts of phone call, read, delivery etc.52* */53const sendReceipt = async (jid, participant, messageIds, type) => {54const node = {55tag: 'receipt',56attrs: {57id: messageIds[0],58},59};60const isReadReceipt = type === 'read' || type === 'read-self';61if (isReadReceipt) {62node.attrs.t = (0, Utils_1.unixTimestampSeconds)().toString();63}64if (type === 'sender' && (0, WABinary_1.isJidUser)(jid)) {65node.attrs.recipient = jid;66node.attrs.to = participant;67}68else {69node.attrs.to = jid;70if (participant) {71node.attrs.participant = participant;72}73}74if (type) {75node.attrs.type = type;76}77const remainingMessageIds = messageIds.slice(1);78if (remainingMessageIds.length) {79node.content = [80{81tag: 'list',82attrs: {},83content: remainingMessageIds.map(id => ({84tag: 'item',85attrs: { id }86}))87}88];89}90logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages');91await sendNode(node);92};93/** Correctly bulk send receipts to multiple chats, participants */94const sendReceipts = async (keys, type) => {95const recps = (0, Utils_1.aggregateMessageKeysNotFromMe)(keys);96for (const { jid, participant, messageIds } of recps) {97await sendReceipt(jid, participant, messageIds, type);98}99};100/** Bulk read messages. Keys can be from different chats & participants */101const readMessages = async (keys) => {102const privacySettings = await fetchPrivacySettings();103// based on privacy settings, we have to change the read type104const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';105await sendReceipts(keys, readType);106};107/** Fetch all the devices we've to send a message to */108const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {109var _a;110const deviceResults = [];111if (!useCache) {112logger.debug('not using cache for devices');113}114const users = [];115jids = Array.from(new Set(jids));116for (let jid of jids) {117const user = (_a = (0, WABinary_1.jidDecode)(jid)) === null || _a === void 0 ? void 0 : _a.user;118jid = (0, WABinary_1.jidNormalizedUser)(jid);119if (userDevicesCache.has(user) && useCache) {120const devices = userDevicesCache.get(user);121deviceResults.push(...devices);122logger.trace({ user }, 'using cache for devices');123}124else {125users.push({ tag: 'user', attrs: { jid } });126}127}128const iq = {129tag: 'iq',130attrs: {131to: WABinary_1.S_WHATSAPP_NET,132type: 'get',133xmlns: 'usync',134},135content: [136{137tag: 'usync',138attrs: {139sid: generateMessageTag(),140mode: 'query',141last: 'true',142index: '0',143context: 'message',144},145content: [146{147tag: 'query',148attrs: {},149content: [150{151tag: 'devices',152attrs: { version: '2' }153}154]155},156{ tag: 'list', attrs: {}, content: users }157]158},159],160};161const result = await query(iq);162const extracted = (0, Utils_1.extractDeviceJids)(result, authState.creds.me.id, ignoreZeroDevices);163const deviceMap = {};164for (const item of extracted) {165deviceMap[item.user] = deviceMap[item.user] || [];166deviceMap[item.user].push(item);167deviceResults.push(item);168}169for (const key in deviceMap) {170userDevicesCache.set(key, deviceMap[key]);171}172return deviceResults;173};174const assertSessions = async (jids, force) => {175let didFetchNewSession = false;176let jidsRequiringFetch = [];177if (force) {178jidsRequiringFetch = jids;179}180else {181const addrs = jids.map(jid => (0, Utils_1.jidToSignalProtocolAddress)(jid).toString());182const sessions = await authState.keys.get('session', addrs);183for (const jid of jids) {184const signalId = (0, Utils_1.jidToSignalProtocolAddress)(jid).toString();185if (!sessions[signalId]) {186jidsRequiringFetch.push(jid);187}188}189}190if (jidsRequiringFetch.length) {191logger.debug({ jidsRequiringFetch }, 'fetching sessions');192const result = await query({193tag: 'iq',194attrs: {195xmlns: 'encrypt',196type: 'get',197to: WABinary_1.S_WHATSAPP_NET,198},199content: [200{201tag: 'key',202attrs: {},203content: jidsRequiringFetch.map(jid => ({204tag: 'user',205attrs: { jid },206}))207}208]209});210await (0, Utils_1.parseAndInjectE2ESessions)(result, authState);211didFetchNewSession = true;212}213return didFetchNewSession;214};215const createParticipantNodes = async (jids, bytes, extraAttrs) => {216let shouldIncludeDeviceIdentity = false;217const nodes = await Promise.all(jids.map(async (jid) => {218const { type, ciphertext } = await (0, Utils_1.encryptSignalProto)(jid, bytes, authState);219if (type === 'pkmsg') {220shouldIncludeDeviceIdentity = true;221}222const node = {223tag: 'to',224attrs: { jid },225content: [{226tag: 'enc',227attrs: {228v: '2',229type,230...extraAttrs || {}231},232content: ciphertext233}]234};235return node;236}));237return { nodes, shouldIncludeDeviceIdentity };238};239const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, useUserDevicesCache, cachedGroupMetadata }) => {240const meId = authState.creds.me.id;241let shouldIncludeDeviceIdentity = false;242const { user, server } = (0, WABinary_1.jidDecode)(jid);243const isGroup = server === 'g.us';244msgId = msgId || (0, Utils_1.generateMessageID)();245useUserDevicesCache = useUserDevicesCache !== false;246const encodedMsg = (0, Utils_1.encodeWAMessage)(message);247const participants = [];248const destinationJid = (0, WABinary_1.jidEncode)(user, isGroup ? 'g.us' : 's.whatsapp.net');249const binaryNodeContent = [];250const devices = [];251if (participant) {252// when the retry request is not for a group253// only send to the specific device that asked for a retry254// otherwise the message is sent out to every device that should be a recipient255if (!isGroup) {256additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };257}258const { user, device } = (0, WABinary_1.jidDecode)(participant.jid);259devices.push({ user, device });260}261await authState.keys.transaction(async () => {262if (isGroup) {263const { ciphertext, senderKeyDistributionMessageKey } = await (0, Utils_1.encryptSenderKeyMsgSignalProto)(destinationJid, encodedMsg, meId, authState);264const [groupData, senderKeyMap] = await Promise.all([265(async () => {266let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;267if (groupData) {268logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');269}270if (!groupData) {271groupData = await groupMetadata(jid);272}273return groupData;274})(),275(async () => {276if (!participant) {277const result = await authState.keys.get('sender-key-memory', [jid]);278return result[jid] || {};279}280return {};281})()282]);283if (!participant) {284const participantsList = groupData.participants.map(p => p.id);285const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);286devices.push(...additionalDevices);287}288const senderKeyJids = [];289// ensure a connection is established with every device290for (const { user, device } of devices) {291const jid = (0, WABinary_1.jidEncode)(user, 's.whatsapp.net', device);292if (!senderKeyMap[jid] || !!participant) {293senderKeyJids.push(jid);294// store that this person has had the sender keys sent to them295senderKeyMap[jid] = true;296}297}298// if there are some participants with whom the session has not been established299// if there are, we re-send the senderkey300if (senderKeyJids.length) {301logger.debug({ senderKeyJids }, 'sending new sender key');302const encSenderKeyMsg = (0, Utils_1.encodeWAMessage)({303senderKeyDistributionMessage: {304axolotlSenderKeyDistributionMessage: senderKeyDistributionMessageKey,305groupId: destinationJid306}307});308await assertSessions(senderKeyJids, false);309const result = await createParticipantNodes(senderKeyJids, encSenderKeyMsg);310shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;311participants.push(...result.nodes);312}313binaryNodeContent.push({314tag: 'enc',315attrs: { v: '2', type: 'skmsg' },316content: ciphertext317});318await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });319}320else {321const { user: meUser } = (0, WABinary_1.jidDecode)(meId);322const encodedMeMsg = (0, Utils_1.encodeWAMessage)({323deviceSentMessage: {324destinationJid,325message326}327});328if (!participant) {329devices.push({ user });330devices.push({ user: meUser });331const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true);332devices.push(...additionalDevices);333}334const allJids = [];335const meJids = [];336const otherJids = [];337for (const { user, device } of devices) {338const jid = (0, WABinary_1.jidEncode)(user, 's.whatsapp.net', device);339const isMe = user === meUser;340if (isMe) {341meJids.push(jid);342}343else {344otherJids.push(jid);345}346allJids.push(jid);347}348await assertSessions(allJids, false);349const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([350createParticipantNodes(meJids, encodedMeMsg),351createParticipantNodes(otherJids, encodedMsg)352]);353participants.push(...meNodes);354participants.push(...otherNodes);355shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;356}357if (participants.length) {358binaryNodeContent.push({359tag: 'participants',360attrs: {},361content: participants362});363}364const stanza = {365tag: 'message',366attrs: {367id: msgId,368type: 'text',369...(additionalAttributes || {})370},371content: binaryNodeContent372};373// if the participant to send to is explicitly specified (generally retry recp)374// ensure the message is only sent to that person375// if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg376if (participant) {377if ((0, WABinary_1.isJidGroup)(destinationJid)) {378stanza.attrs.to = destinationJid;379stanza.attrs.participant = participant.jid;380}381else if ((0, WABinary_1.areJidsSameUser)(participant.jid, meId)) {382stanza.attrs.to = participant.jid;383stanza.attrs.recipient = destinationJid;384}385else {386stanza.attrs.to = participant.jid;387}388}389else {390stanza.attrs.to = destinationJid;391}392if (shouldIncludeDeviceIdentity) {393stanza.content.push({394tag: 'device-identity',395attrs: {},396content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)397});398logger.debug({ jid }, 'adding device identity');399}400logger.debug({ msgId }, `sending message to ${participants.length} devices`);401await sendNode(stanza);402});403return msgId;404};405const getPrivacyTokens = async (jids) => {406const t = (0, Utils_1.unixTimestampSeconds)().toString();407const result = await query({408tag: 'iq',409attrs: {410to: WABinary_1.S_WHATSAPP_NET,411type: 'set',412xmlns: 'privacy'413},414content: [415{416tag: 'tokens',417attrs: {},418content: jids.map(jid => ({419tag: 'token',420attrs: {421jid: (0, WABinary_1.jidNormalizedUser)(jid),422t,423type: 'trusted_contact'424}425}))426}427]428});429return result;430};431const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn);432const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update');433return {434...sock,435getPrivacyTokens,436assertSessions,437relayMessage,438sendReceipt,439sendReceipts,440readMessages,441refreshMediaConn,442waUploadToServer,443fetchPrivacySettings,444updateMediaMessage: async (message) => {445const content = (0, Utils_1.assertMediaContent)(message.message);446const mediaKey = content.mediaKey;447const meId = authState.creds.me.id;448const node = (0, Utils_1.encryptMediaRetryRequest)(message.key, mediaKey, meId);449let error = undefined;450await Promise.all([451sendNode(node),452waitForMsgMediaUpdate(update => {453const result = update.find(c => c.key.id === message.key.id);454if (result) {455if (result.error) {456error = result.error;457}458else {459try {460const media = (0, Utils_1.decryptMediaRetryData)(result.media, mediaKey, result.key.id);461if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {462const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result];463throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: (0, Utils_1.getStatusCodeForMediaRetry)(media.result) || 404 });464}465content.directPath = media.directPath;466content.url = (0, Utils_1.getUrlFromDirectPath)(content.directPath);467logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');468}469catch (err) {470error = err;471}472}473return true;474}475})476]);477if (error) {478throw error;479}480ev.emit('messages.update', [481{ key: message.key, update: { message: message.message } }482]);483return message;484},485sendMessage: async (jid, content, options = {}) => {486var _a, _b;487const userJid = authState.creds.me.id;488if (typeof content === 'object' &&489'disappearingMessagesInChat' in content &&490typeof content['disappearingMessagesInChat'] !== 'undefined' &&491(0, WABinary_1.isJidGroup)(jid)) {492const { disappearingMessagesInChat } = content;493const value = typeof disappearingMessagesInChat === 'boolean' ?494(disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :495disappearingMessagesInChat;496await groupToggleEphemeral(jid, value);497}498else {499const fullMsg = await (0, Utils_1.generateWAMessage)(jid, content, {500logger,501userJid,502getUrlInfo: text => (0, link_preview_1.getUrlInfo)(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, timeoutMs: 3000 }),503upload: waUploadToServer,504mediaCache: config.mediaCache,505...options,506});507const isDeleteMsg = 'delete' in content && !!content.delete;508const additionalAttributes = {};509// required for delete510if (isDeleteMsg) {511// if the chat is a group, and I am not the author, then delete the message as an admin512if ((0, WABinary_1.isJidGroup)((_a = content.delete) === null || _a === void 0 ? void 0 : _a.remoteJid) && !((_b = content.delete) === null || _b === void 0 ? void 0 : _b.fromMe)) {513additionalAttributes.edit = '8';514}515else {516additionalAttributes.edit = '7';517}518}519fullMsg.message = (0, Utils_1.patchMessageForMdIfRequired)(fullMsg.message);520await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, cachedGroupMetadata: options.cachedGroupMetadata, additionalAttributes });521if (config.emitOwnEvents) {522process.nextTick(() => {523upsertMessage(fullMsg, 'append');524});525}526return fullMsg;527}528}529};530};531exports.makeMessagesSocket = makeMessagesSocket;532533534