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