Path: blob/master/node_modules/@adiwajshing/baileys/lib/LegacySocket/messages.js
2593 views
"use strict";1var __importDefault = (this && this.__importDefault) || function (mod) {2return (mod && mod.__esModule) ? mod : { "default": mod };3};4Object.defineProperty(exports, "__esModule", { value: true });5const boom_1 = require("@hapi/boom");6const WAProto_1 = require("../../WAProto");7const Defaults_1 = require("../Defaults");8const Types_1 = require("../Types");9const Utils_1 = require("../Utils");10const WABinary_1 = require("../WABinary");11const chats_1 = __importDefault(require("./chats"));12const STATUS_MAP = {13read: Types_1.WAMessageStatus.READ,14message: Types_1.WAMessageStatus.DELIVERY_ACK,15error: Types_1.WAMessageStatus.ERROR16};17const makeMessagesSocket = (config) => {18const { logger } = config;19const sock = chats_1.default(config);20const { ev, ws: socketEvents, query, generateMessageTag, currentEpoch, setQuery, state } = sock;21let mediaConn;22const refreshMediaConn = async (forceGet = false) => {23const media = await mediaConn;24if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {25mediaConn = (async () => {26const { media_conn } = await query({27json: ['query', 'mediaConn'],28requiresPhoneConnection: false,29expect200: true30});31media_conn.fetchDate = new Date();32return media_conn;33})();34}35return mediaConn;36};37const fetchMessagesFromWA = async (jid, count, cursor) => {38let key;39if (cursor) {40key = 'before' in cursor ? cursor.before : cursor.after;41}42const { content } = await query({43json: {44tag: 'query',45attrs: {46epoch: currentEpoch().toString(),47type: 'message',48jid: jid,49kind: !cursor || 'before' in cursor ? 'before' : 'after',50count: count.toString(),51index: key === null || key === void 0 ? void 0 : key.id,52owner: (key === null || key === void 0 ? void 0 : key.fromMe) === false ? 'false' : 'true',53}54},55binaryTag: [Types_1.WAMetric.queryMessages, Types_1.WAFlag.ignore],56expect200: false,57requiresPhoneConnection: true58});59if (Array.isArray(content)) {60return content.map(data => WAProto_1.proto.WebMessageInfo.decode(data.content));61}62return [];63};64const updateMediaMessage = async (message) => {65var _a, _b, _c, _d, _e;66const content = ((_a = message.message) === null || _a === void 0 ? void 0 : _a.audioMessage) || ((_b = message.message) === null || _b === void 0 ? void 0 : _b.videoMessage) || ((_c = message.message) === null || _c === void 0 ? void 0 : _c.imageMessage) || ((_d = message.message) === null || _d === void 0 ? void 0 : _d.stickerMessage) || ((_e = message.message) === null || _e === void 0 ? void 0 : _e.documentMessage);67if (!content) {68throw new boom_1.Boom(`given message ${message.key.id} is not a media message`, { statusCode: 400, data: message });69}70const response = await query({71json: {72tag: 'query',73attrs: {74type: 'media',75index: message.key.id,76owner: message.key.fromMe ? 'true' : 'false',77jid: message.key.remoteJid,78epoch: currentEpoch().toString()79}80},81binaryTag: [Types_1.WAMetric.queryMedia, Types_1.WAFlag.ignore],82expect200: true,83requiresPhoneConnection: true84});85const attrs = response.attrs;86Object.assign(content, attrs); // update message87ev.emit('messages.upsert', { messages: [message], type: 'replace' });88return response;89};90const onMessage = (message, type) => {91var _a, _b, _c, _d;92const jid = message.key.remoteJid;93// store chat updates in this94const chatUpdate = {95id: jid,96};97const emitGroupUpdate = (update) => {98ev.emit('groups.update', [{ id: jid, ...update }]);99};100if (message.message) {101chatUpdate.conversationTimestamp = +Utils_1.toNumber(message.messageTimestamp);102// add to count if the message isn't from me & there exists a message103if (!message.key.fromMe) {104chatUpdate.unreadCount = 1;105const participant = WABinary_1.jidNormalizedUser(message.participant || jid);106ev.emit('presence.update', {107id: jid,108presences: { [participant]: { lastKnownPresence: 'available' } }109});110}111}112const protocolMessage = ((_a = message.message) === null || _a === void 0 ? void 0 : _a.protocolMessage) || ((_d = (_c = (_b = message.message) === null || _b === void 0 ? void 0 : _b.ephemeralMessage) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.protocolMessage);113// if it's a message to delete another message114if (protocolMessage) {115switch (protocolMessage.type) {116case WAProto_1.proto.ProtocolMessage.ProtocolMessageType.REVOKE:117const key = protocolMessage.key;118const messageStubType = Types_1.WAMessageStubType.REVOKE;119ev.emit('messages.update', [120{121// the key of the deleted message is updated122update: { message: null, key: message.key, messageStubType },123key124}125]);126return;127case WAProto_1.proto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING:128chatUpdate.ephemeralSettingTimestamp = message.messageTimestamp;129chatUpdate.ephemeralExpiration = protocolMessage.ephemeralExpiration;130if (WABinary_1.isJidGroup(jid)) {131emitGroupUpdate({ ephemeralDuration: protocolMessage.ephemeralExpiration || null });132}133break;134default:135break;136}137}138// check if the message is an action139if (message.messageStubType) {140const { user } = state.legacy;141//let actor = jidNormalizedUser (message.participant)142let participants;143const emitParticipantsUpdate = (action) => (ev.emit('group-participants.update', { id: jid, participants, action }));144switch (message.messageStubType) {145case Types_1.WAMessageStubType.CHANGE_EPHEMERAL_SETTING:146chatUpdate.ephemeralSettingTimestamp = message.messageTimestamp;147chatUpdate.ephemeralExpiration = +message.messageStubParameters[0];148if (WABinary_1.isJidGroup(jid)) {149emitGroupUpdate({ ephemeralDuration: +message.messageStubParameters[0] || null });150}151break;152case Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE:153case Types_1.WAMessageStubType.GROUP_PARTICIPANT_REMOVE:154participants = message.messageStubParameters.map(WABinary_1.jidNormalizedUser);155emitParticipantsUpdate('remove');156// mark the chat read only if you left the group157if (participants.includes(user.id)) {158chatUpdate.readOnly = true;159}160break;161case Types_1.WAMessageStubType.GROUP_PARTICIPANT_ADD:162case Types_1.WAMessageStubType.GROUP_PARTICIPANT_INVITE:163case Types_1.WAMessageStubType.GROUP_PARTICIPANT_ADD_REQUEST_JOIN:164participants = message.messageStubParameters.map(WABinary_1.jidNormalizedUser);165if (participants.includes(user.id)) {166chatUpdate.readOnly = null;167}168emitParticipantsUpdate('add');169break;170case Types_1.WAMessageStubType.GROUP_CHANGE_ANNOUNCE:171const announce = message.messageStubParameters[0] === 'on';172emitGroupUpdate({ announce });173break;174case Types_1.WAMessageStubType.GROUP_CHANGE_RESTRICT:175const restrict = message.messageStubParameters[0] === 'on';176emitGroupUpdate({ restrict });177break;178case Types_1.WAMessageStubType.GROUP_CHANGE_SUBJECT:179case Types_1.WAMessageStubType.GROUP_CREATE:180chatUpdate.name = message.messageStubParameters[0];181emitGroupUpdate({ subject: chatUpdate.name });182break;183}184}185if (Object.keys(chatUpdate).length > 1) {186ev.emit('chats.update', [chatUpdate]);187}188ev.emit('messages.upsert', { messages: [message], type });189};190const waUploadToServer = Utils_1.getWAUploadToServer(config, refreshMediaConn);191/** Query a string to check if it has a url, if it does, return WAUrlInfo */192const generateUrlInfo = async (text) => {193const response = await query({194json: {195tag: 'query',196attrs: {197type: 'url',198url: text,199epoch: currentEpoch().toString()200}201},202binaryTag: [26, Types_1.WAFlag.ignore],203expect200: true,204requiresPhoneConnection: false205});206const urlInfo = { ...response.attrs };207if (response && response.content) {208urlInfo.jpegThumbnail = response.content;209}210return urlInfo;211};212/** Relay (send) a WAMessage; more advanced functionality to send a built WA Message, you may want to stick with sendMessage() */213const relayMessage = async (message, { waitForAck } = { waitForAck: true }) => {214var _a;215const json = {216tag: 'action',217attrs: { epoch: currentEpoch().toString(), type: 'relay' },218content: [219{220tag: 'message',221attrs: {},222content: WAProto_1.proto.WebMessageInfo.encode(message).finish()223}224]225};226const isMsgToMe = WABinary_1.areJidsSameUser(message.key.remoteJid, ((_a = state.legacy.user) === null || _a === void 0 ? void 0 : _a.id) || '');227const flag = isMsgToMe ? Types_1.WAFlag.acknowledge : Types_1.WAFlag.ignore; // acknowledge when sending message to oneself228const mID = message.key.id;229const finalState = isMsgToMe ? Types_1.WAMessageStatus.READ : Types_1.WAMessageStatus.SERVER_ACK;230message.status = Types_1.WAMessageStatus.PENDING;231const promise = query({232json,233binaryTag: [Types_1.WAMetric.message, flag],234tag: mID,235expect200: true,236requiresPhoneConnection: true237});238if (waitForAck) {239await promise;240message.status = finalState;241}242else {243const emitUpdate = (status) => {244message.status = status;245ev.emit('messages.update', [{ key: message.key, update: { status } }]);246};247promise248.then(() => emitUpdate(finalState))249.catch(() => emitUpdate(Types_1.WAMessageStatus.ERROR));250}251if (config.emitOwnEvents) {252onMessage(message, 'append');253}254};255// messages received256const messagesUpdate = (node, isLatest) => {257const messages = WABinary_1.getBinaryNodeMessages(node);258messages.reverse();259ev.emit('messages.set', { messages, isLatest });260};261socketEvents.on('CB:action,add:last', json => messagesUpdate(json, true));262socketEvents.on('CB:action,add:unread', json => messagesUpdate(json, false));263socketEvents.on('CB:action,add:before', json => messagesUpdate(json, false));264// new messages265socketEvents.on('CB:action,add:relay,message', (node) => {266const msgs = WABinary_1.getBinaryNodeMessages(node);267for (const msg of msgs) {268onMessage(msg, 'notify');269}270});271// If a message has been updated272// usually called when a video message gets its upload url, or live locations or ciphertext message gets fixed273socketEvents.on('CB:action,add:update,message', (node) => {274const msgs = WABinary_1.getBinaryNodeMessages(node);275for (const msg of msgs) {276onMessage(msg, 'replace');277}278});279// message status updates280const onMessageStatusUpdate = ({ content }) => {281if (Array.isArray(content)) {282const updates = [];283for (const { attrs: json } of content) {284const key = {285remoteJid: WABinary_1.jidNormalizedUser(json.jid),286id: json.index,287fromMe: json.owner === 'true'288};289const status = STATUS_MAP[json.type];290if (status) {291updates.push({ key, update: { status } });292}293else {294logger.warn({ content, key }, 'got unknown status update for message');295}296}297ev.emit('messages.update', updates);298}299};300const onMessageInfoUpdate = ([, attributes]) => {301var _a, _b;302let ids = attributes.id;303if (typeof ids === 'string') {304ids = [ids];305}306let updateKey;307switch (attributes.ack.toString()) {308case '2':309updateKey = 'receiptTimestamp';310break;311case '3':312updateKey = 'readTimestamp';313break;314case '4':315updateKey = 'playedTimestamp';316break;317default:318logger.warn({ attributes }, 'received unknown message info update');319return;320}321const keyPartial = {322remoteJid: WABinary_1.jidNormalizedUser(attributes.to),323fromMe: WABinary_1.areJidsSameUser(attributes.from, ((_b = (_a = state.legacy) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.id) || ''),324};325const userJid = WABinary_1.jidNormalizedUser(attributes.participant || attributes.to);326const updates = ids.map(id => ({327key: { ...keyPartial, id },328receipt: {329userJid,330[updateKey]: +attributes.t331}332}));333ev.emit('message-receipt.update', updates);334// for individual messages335// it means the message is marked read/delivered336if (!WABinary_1.isJidGroup(keyPartial.remoteJid)) {337ev.emit('messages.update', ids.map(id => ({338key: { ...keyPartial, id },339update: {340status: updateKey === 'receiptTimestamp' ? Types_1.WAMessageStatus.DELIVERY_ACK : Types_1.WAMessageStatus.READ341}342})));343}344};345socketEvents.on('CB:action,add:relay,received', onMessageStatusUpdate);346socketEvents.on('CB:action,,received', onMessageStatusUpdate);347socketEvents.on('CB:Msg', onMessageInfoUpdate);348socketEvents.on('CB:MsgInfo', onMessageInfoUpdate);349return {350...sock,351relayMessage,352generateUrlInfo,353messageInfo: async (jid, messageID) => {354const { content } = await query({355json: {356tag: 'query',357attrs: {358type: 'message_info',359index: messageID,360jid: jid,361epoch: currentEpoch().toString()362}363},364binaryTag: [Types_1.WAMetric.queryRead, Types_1.WAFlag.ignore],365expect200: true,366requiresPhoneConnection: true367});368const info = {};369if (Array.isArray(content)) {370for (const { tag, content: innerData } of content) {371const [{ attrs }] = innerData;372const jid = WABinary_1.jidNormalizedUser(attrs.jid);373const recp = info[jid] || { userJid: jid };374const date = +attrs.t;375switch (tag) {376case 'read':377recp.readTimestamp = date;378break;379case 'delivery':380recp.receiptTimestamp = date;381break;382}383info[jid] = recp;384}385}386return Object.values(info);387},388downloadMediaMessage: async (message, type = 'buffer') => {389const downloadMediaMessage = async () => {390const mContent = Utils_1.extractMessageContent(message.message);391if (!mContent) {392throw new boom_1.Boom('No message present', { statusCode: 400, data: message });393}394const stream = await Utils_1.decryptMediaMessageBuffer(mContent);395if (type === 'buffer') {396let buffer = Buffer.from([]);397for await (const chunk of stream) {398buffer = Buffer.concat([buffer, chunk]);399}400return buffer;401}402return stream;403};404try {405const result = await downloadMediaMessage();406return result;407}408catch (error) {409if (error.message.includes('404')) { // media needs to be updated410logger.info(`updating media of message: ${message.key.id}`);411await updateMediaMessage(message);412const result = await downloadMediaMessage();413return result;414}415throw error;416}417},418updateMediaMessage,419fetchMessagesFromWA,420/** Load a single message specified by the ID */421loadMessageFromWA: async (jid, id) => {422// load the message before the given message423let messages = (await fetchMessagesFromWA(jid, 1, { before: { id, fromMe: true } }));424if (!messages[0]) {425messages = (await fetchMessagesFromWA(jid, 1, { before: { id, fromMe: false } }));426}427// the message after the loaded message is the message required428const [actual] = await fetchMessagesFromWA(jid, 1, { after: messages[0] && messages[0].key });429return actual;430},431searchMessages: async (txt, inJid, count, page) => {432var _a;433const node = await query({434json: {435tag: 'query',436attrs: {437epoch: currentEpoch().toString(),438type: 'search',439search: txt,440count: count.toString(),441page: page.toString(),442jid: inJid443}444},445binaryTag: [24, Types_1.WAFlag.ignore],446expect200: true447}); // encrypt and send off448return {449last: ((_a = node.attrs) === null || _a === void 0 ? void 0 : _a.last) === 'true',450messages: WABinary_1.getBinaryNodeMessages(node)451};452},453sendMessage: async (jid, content, options = { waitForAck: true }) => {454var _a;455const userJid = (_a = state.legacy.user) === null || _a === void 0 ? void 0 : _a.id;456if (typeof content === 'object' &&457'disappearingMessagesInChat' in content &&458typeof content['disappearingMessagesInChat'] !== 'undefined' &&459WABinary_1.isJidGroup(jid)) {460const { disappearingMessagesInChat } = content;461const value = typeof disappearingMessagesInChat === 'boolean' ?462(disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :463disappearingMessagesInChat;464const tag = generateMessageTag(true);465await setQuery([466{467tag: 'group',468attrs: { id: tag, jid, type: 'prop', author: userJid },469content: [470{ tag: 'ephemeral', attrs: { value: value.toString() } }471]472}473]);474}475else {476const msg = await Utils_1.generateWAMessage(jid, content, {477logger,478userJid: userJid,479getUrlInfo: generateUrlInfo,480upload: waUploadToServer,481mediaCache: config.mediaCache,482...options,483});484await relayMessage(msg, { waitForAck: options.waitForAck });485return msg;486}487}488};489};490exports.default = makeMessagesSocket;491492493