From 565015c0364011216fb75899a5fbee6fff922fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Sat, 10 Jun 2023 07:57:46 +0300 Subject: [PATCH 01/32] extended Message.forward - added ability to forward messages with original caption text --- src/structures/Message.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index a3b311796..8ed6baf2e 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -363,22 +363,35 @@ class Message extends Base { async acceptGroupV4Invite() { return await this.client.acceptGroupV4Invite(this.inviteV4); } + + /** + * Forward options: + * + * @typedef {Object} MessageForwardOptions + * @property {boolean} [multicast=false] + * @property {boolean} [withCaption=false] Forwards this message with the caption text of the original message if provided. + */ /** * Forwards this message to another chat (that you chatted before, otherwise it will fail) * * @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded + * @param {MessageForwardOptions} [options] Options used when forwarding the message * @returns {Promise} */ - async forward(chat) { + async forward(chat, options = {}) { const chatId = typeof chat === 'string' ? chat : chat.id._serialized; + let internalOptions = { + multicast: options.multicast || false, + withCaption: options.withCaption || false + } - await this.client.pupPage.evaluate(async (msgId, chatId) => { + await this.client.pupPage.evaluate(async (msgId, chatId, options) => { let msg = window.Store.Msg.get(msgId); let chat = window.Store.Chat.get(chatId); - return await chat.forwardMessages([msg]); - }, this.id._serialized, chatId); + return await chat.forwardMessages([msg], ...Object.values(options)); + }, this.id._serialized, chatId, internalOptions); } /** From eaf507279451a5ea9c519fd98f54563139c53406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Sat, 10 Jun 2023 07:58:53 +0300 Subject: [PATCH 02/32] docs updated accordingly --- index.d.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 4b686a69d..130e97ed5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -827,7 +827,7 @@ declare namespace WAWebJS { /** * Forwards this message to another chat (that you chatted before, otherwise it will fail) */ - forward: (chat: Chat | string) => Promise, + forward: (chat: Chat | string, options?: MessageForwardOptions) => Promise, /** Star this message */ star: () => Promise, /** Unstar this message */ @@ -911,6 +911,13 @@ declare namespace WAWebJS { stickerCategories?: string[] } + /** Options for forwarding a message */ + export interface MessageForwardOptions { + multicast?: boolean + /** Adds caption text to forwarded message (if provided), if withCaption is true */ + withCaption?: boolean + } + export interface MediaFromURLOptions { client?: Client filename?: string From 9456c65097f1fd8779f4eaa0b9d77dabc062ef6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Sat, 10 Jun 2023 07:59:16 +0300 Subject: [PATCH 03/32] added usage example --- example.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/example.js b/example.js index 065fd123e..3a10fd7c6 100644 --- a/example.js +++ b/example.js @@ -202,6 +202,20 @@ client.on('message', async msg => { } else if (msg.body === '!reaction') { msg.react('👍'); } + else if (msg.author) { + /** Note: forwarding with caption is available for media messages (photo/video) only. */ + + /** + * Let's say the message was sent in a group + * and I want to forward it to its author. + */ + + // 1. By default it will be forwarded without a caption text (even if provided): + await msg.forward(msg.author) + + // 2. To forward with a caption text of original message (if provided) do: + await msg.forward(msg.author, { withCaption: true }) + } }); client.on('message_create', (msg) => { From 677079c1670745ba2ba18021fc7c15b11d56d48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Sat, 10 Jun 2023 12:15:55 +0300 Subject: [PATCH 04/32] ESLint fixes --- example.js | 4 ++-- src/structures/Message.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example.js b/example.js index 3a10fd7c6..47c1d2b43 100644 --- a/example.js +++ b/example.js @@ -211,10 +211,10 @@ client.on('message', async msg => { */ // 1. By default it will be forwarded without a caption text (even if provided): - await msg.forward(msg.author) + await msg.forward(msg.author); // 2. To forward with a caption text of original message (if provided) do: - await msg.forward(msg.author, { withCaption: true }) + await msg.forward(msg.author, { withCaption: true }); } }); diff --git a/src/structures/Message.js b/src/structures/Message.js index 8ed6baf2e..cd3adae1f 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -384,7 +384,7 @@ class Message extends Base { let internalOptions = { multicast: options.multicast || false, withCaption: options.withCaption || false - } + }; await this.client.pupPage.evaluate(async (msgId, chatId, options) => { let msg = window.Store.Msg.get(msgId); From 5f1ca95f9d70c009548d995864affb955c1f9d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Fri, 16 Jun 2023 05:11:26 +0300 Subject: [PATCH 05/32] withCaption value is changed to true by default --- example.js | 12 ++++++------ index.d.ts | 5 ++++- src/structures/Message.js | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/example.js b/example.js index 60bd34192..9f901b425 100644 --- a/example.js +++ b/example.js @@ -212,18 +212,18 @@ client.on('message', async msg => { } } else if (msg.author) { - /** Note: forwarding with caption is available for media messages (photo/video) only. */ - /** + * Note: forwarding with caption is available for media messages (photo/video) only. + * * Let's say the message was sent in a group - * and I want to forward it to its author. + * and you want to forward it to its author. */ - // 1. By default it will be forwarded without a caption text (even if provided): + // 1. By default it will be forwarded with a caption text (if provided): await msg.forward(msg.author); - // 2. To forward with a caption text of original message (if provided) do: - await msg.forward(msg.author, { withCaption: true }); + // 2. To forward without a caption text (if provided) do: + await msg.forward(msg.author, { withCaption: false }); } }); diff --git a/index.d.ts b/index.d.ts index 5e307ea8b..9d6e99467 100644 --- a/index.d.ts +++ b/index.d.ts @@ -931,7 +931,10 @@ declare namespace WAWebJS { /** Options for forwarding a message */ export interface MessageForwardOptions { multicast?: boolean - /** Adds caption text to forwarded message (if provided), if withCaption is true */ + /** + * Adds caption text to forwarded message (if provided). + * Value is true by default. + */ withCaption?: boolean } diff --git a/src/structures/Message.js b/src/structures/Message.js index 9b47d1dde..ac4a1a1a5 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -380,7 +380,7 @@ class Message extends Base { * * @typedef {Object} MessageForwardOptions * @property {boolean} [multicast=false] - * @property {boolean} [withCaption=false] Forwards this message with the caption text of the original message if provided. + * @property {boolean} [withCaption=true] Forwards this message with the caption text of the original message if provided. */ /** @@ -394,7 +394,7 @@ class Message extends Base { const chatId = typeof chat === 'string' ? chat : chat.id._serialized; let internalOptions = { multicast: options.multicast || false, - withCaption: options.withCaption || false + withCaption: options.withCaption === false ? false : true }; await this.client.pupPage.evaluate(async (msgId, chatId, options) => { From a90b02de0fbc4ac591a8403dcfcf423f5d95d392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Fri, 16 Jun 2023 06:04:15 +0300 Subject: [PATCH 06/32] fix: forwarded message is a first message in the chat history --- src/structures/Message.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index ac4a1a1a5..c2a0a9571 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -398,8 +398,9 @@ class Message extends Base { }; await this.client.pupPage.evaluate(async (msgId, chatId, options) => { - let msg = window.Store.Msg.get(msgId); - let chat = window.Store.Chat.get(chatId); + const chatWid = window.Store.WidFactory.createWid(chatId); + const chat = await window.Store.Chat.find(chatWid); + const msg = window.Store.Msg.get(msgId); return await chat.forwardMessages([msg], ...Object.values(options)); }, this.id._serialized, chatId, internalOptions); From 924fd3562011d4f700a9af7cbfa3594f53b5bd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Mon, 19 Jun 2023 23:36:22 +0300 Subject: [PATCH 07/32] added support for forwarding attachments with caption --- src/structures/Message.js | 10 ++-- src/util/Injected.js | 104 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index c2a0a9571..0a4477dc7 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -397,13 +397,13 @@ class Message extends Base { withCaption: options.withCaption === false ? false : true }; - await this.client.pupPage.evaluate(async (msgId, chatId, options) => { + await this.client.pupPage.evaluate(async (chatId, msgId, options) => { const chatWid = window.Store.WidFactory.createWid(chatId); const chat = await window.Store.Chat.find(chatWid); - const msg = window.Store.Msg.get(msgId); - - return await chat.forwardMessages([msg], ...Object.values(options)); - }, this.id._serialized, chatId, internalOptions); + const message = window.Store.Msg.get(msgId); + + return await window.WWebJS.forwardMessage(chat, message, options); + }, chatId, this.id._serialized, internalOptions); } /** diff --git a/src/util/Injected.js b/src/util/Injected.js index 73c00dd05..4d6c4e597 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -22,6 +22,9 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.NumberInfo = window.mR.findModule('formattedPhoneNumber')[0]; window.Store.MediaTypes = window.mR.findModule('msgToMediaType')[0]; window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0]; + window.Store.getAsMms = window.mR.findModule('getAsMms')[0].getAsMms; + window.Store.getIsSentByMe = window.mR.findModule('getIsSentByMe')[0].getIsSentByMe; + window.Store.isForwardMediaWithCaptionsEnabled = window.mR.findModule('isForwardMediaWithCaptionsEnabled')[0].isForwardMediaWithCaptionsEnabled; window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default; window.Store.MessageInfo = window.mR.findModule('sendQueryMsgInfo')[0]; window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default; @@ -290,6 +293,107 @@ exports.LoadUtils = () => { await window.Store.SendMessage.addAndSendMsgToChat(chat, message); return window.Store.Msg.get(newMsgId._serialized); }; + + window.WWebJS.forwardMessage = async (chat, msg, options = {}) => { + const chatId = chat.id; + const contact = chat.contact; + + if (contact.isUser && contact.isContactBlocked) { + throw new Error("Attempted forwarding to a blocked contact", contact); + } + + const isMediaMsg = (msg) => { + return Boolean(window.Store.getAsMms(msg) && !msg.ctwaContext) + }; + + if (isMediaMsg(msg)) { + return await window.WWebJS.forwardMediaMessage(chat, msg, options); + } + + return await chat._forwardMessageAndSendToChat(msg, chatId, ...Object.values(options)); + } + + window.WWebJS.forwardMediaMessage = async (chat, msg, options = {}) => { + const { multicast: multicast, withCaption: withCaption } = options; + const mediaObject = msg.mediaObject; + + if (!mediaObject) { + throw new Error('Forwarding media message without media object', msg); + } + + const serializedMediaData = msg.mediaData.toJSON(); + + if (serializedMediaData.preview != null) { + (serializedMediaData.preview = mediaObject.contentInfo._preview); + } + + if (serializedMediaData.mediaBlob instanceof window.Store.OpaqueData) { + serializedMediaData.mediaBlob.retain(); + } + + const mimetype = { + mimetype: serializedMediaData.mimetype + } + + let mediaType; + serializedMediaData.isGif ? + mediaType = { + ...mimetype, + isGif: true + } : mediaType = mimetype; + + if (serializedMediaData.type === 'ptt') { + serializedMediaData.type = 'audio' + } + + const productMsgOptions = { + businessOwnerJid: msg.businessOwnerJid, + productId: msg.productId, + currencyCode: msg.currencyCode, + priceAmount1000: msg.priceAmount1000, + salePriceAmount1000: msg.salePriceAmount1000, + retailerId: msg.retailerId, + url: msg.url, + productImageCount: msg.productImageCount, + title: msg.title, + description: msg.description + } + + const docInTempl = + serializedMediaData.type === 'document' && + (msg.isFromTemplate || msg.isDynamicReplyButtonsMsg); + + let caption = docInTempl || serializedMediaData.type === 'product' ? + msg.caption : ''; + + if (window.Store.isForwardMediaWithCaptionsEnabled() && + withCaption && + msg.isCaptionByUser) { + caption = msg.caption; + } + + const mediaPrep = new window.Store.MediaPrep.MediaPrep( + serializedMediaData.type, + Promise.resolve(serializedMediaData) + ); + + const mediaMessageOptions = { + forwardedFromWeb: true, + caption: caption, + mentionedJidList: msg.mentionedJidList, + groupMentions: msg.groupMentions, + footer: msg.type === 'product' ? msg.footer : undefined, + addEvenWhilePreparing: true, + placeholderProps: mediaType, + isForwarded: msg.isForwarded || !window.Store.getIsSentByMe(msg), + forwardingScore: msg.getForwardingScoreWhenForwarded(), + multicast: multicast, + productMsgOptions: productMsgOptions, + isAvatar: msg.isAvatar !== null && msg.isAvatar !== undefined && msg.isAvatar + } + + await mediaPrep.sendToChat(chat, mediaMessageOptions); + } window.WWebJS.editMessage = async (msg, content, options = {}) => { From 3c3a629121988c7a3a0cd001afce558b5eec8b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Mon, 19 Jun 2023 23:38:29 +0300 Subject: [PATCH 08/32] example updated --- example.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example.js b/example.js index 9f901b425..ec3c9c378 100644 --- a/example.js +++ b/example.js @@ -213,7 +213,8 @@ client.on('message', async msg => { } else if (msg.author) { /** - * Note: forwarding with caption is available for media messages (photo/video) only. + * Note: forwarding with caption is available for media messages (photo/video) + * and messages with attachments (documets) also. * * Let's say the message was sent in a group * and you want to forward it to its author. From 7e62a0239d34dca21400cea2d68090b1b2cf839e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Tue, 20 Jun 2023 00:54:08 +0300 Subject: [PATCH 09/32] ESLint fixes --- src/util/Injected.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 4d6c4e597..76e4ede05 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -299,11 +299,11 @@ exports.LoadUtils = () => { const contact = chat.contact; if (contact.isUser && contact.isContactBlocked) { - throw new Error("Attempted forwarding to a blocked contact", contact); + throw new Error('Attempted forwarding to a blocked contact', contact); } const isMediaMsg = (msg) => { - return Boolean(window.Store.getAsMms(msg) && !msg.ctwaContext) + return Boolean(window.Store.getAsMms(msg) && !msg.ctwaContext); }; if (isMediaMsg(msg)) { @@ -311,7 +311,7 @@ exports.LoadUtils = () => { } return await chat._forwardMessageAndSendToChat(msg, chatId, ...Object.values(options)); - } + }; window.WWebJS.forwardMediaMessage = async (chat, msg, options = {}) => { const { multicast: multicast, withCaption: withCaption } = options; @@ -333,7 +333,7 @@ exports.LoadUtils = () => { const mimetype = { mimetype: serializedMediaData.mimetype - } + }; let mediaType; serializedMediaData.isGif ? @@ -343,7 +343,7 @@ exports.LoadUtils = () => { } : mediaType = mimetype; if (serializedMediaData.type === 'ptt') { - serializedMediaData.type = 'audio' + serializedMediaData.type = 'audio'; } const productMsgOptions = { @@ -357,7 +357,7 @@ exports.LoadUtils = () => { productImageCount: msg.productImageCount, title: msg.title, description: msg.description - } + }; const docInTempl = serializedMediaData.type === 'document' && @@ -390,10 +390,10 @@ exports.LoadUtils = () => { multicast: multicast, productMsgOptions: productMsgOptions, isAvatar: msg.isAvatar !== null && msg.isAvatar !== undefined && msg.isAvatar - } + }; await mediaPrep.sendToChat(chat, mediaMessageOptions); - } + }; window.WWebJS.editMessage = async (msg, content, options = {}) => { From bd17e61231d302d6cd090d538ef683ecb7581251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Wed, 21 Jun 2023 20:20:07 +0300 Subject: [PATCH 10/32] docs clarification --- index.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.d.ts b/index.d.ts index 9d6e99467..e7f5a4a5e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -930,10 +930,14 @@ declare namespace WAWebJS { /** Options for forwarding a message */ export interface MessageForwardOptions { + /** + * @default false + */ multicast?: boolean /** * Adds caption text to forwarded message (if provided). * Value is true by default. + * @default true */ withCaption?: boolean } From 255da5b6b360aa844301abff4a070cbbb941d944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Thu, 22 Jun 2023 02:37:35 +0300 Subject: [PATCH 11/32] code refactoring --- src/util/Injected.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 76e4ede05..8f683c4aa 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -24,7 +24,6 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0]; window.Store.getAsMms = window.mR.findModule('getAsMms')[0].getAsMms; window.Store.getIsSentByMe = window.mR.findModule('getIsSentByMe')[0].getIsSentByMe; - window.Store.isForwardMediaWithCaptionsEnabled = window.mR.findModule('isForwardMediaWithCaptionsEnabled')[0].isForwardMediaWithCaptionsEnabled; window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default; window.Store.MessageInfo = window.mR.findModule('sendQueryMsgInfo')[0]; window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default; @@ -302,11 +301,11 @@ exports.LoadUtils = () => { throw new Error('Attempted forwarding to a blocked contact', contact); } - const isMediaMsg = (msg) => { + const _isMediaMsg = (msg) => { return Boolean(window.Store.getAsMms(msg) && !msg.ctwaContext); }; - if (isMediaMsg(msg)) { + if (_isMediaMsg(msg)) { return await window.WWebJS.forwardMediaMessage(chat, msg, options); } @@ -359,18 +358,21 @@ exports.LoadUtils = () => { description: msg.description }; - const docInTempl = + const isImageOrVideo = + serializedMediaData.type === 'image' || + serializedMediaData.type === 'video'; + + const isBtnOrList = serializedMediaData.type === 'document' && (msg.isFromTemplate || msg.isDynamicReplyButtonsMsg); - let caption = docInTempl || serializedMediaData.type === 'product' ? - msg.caption : ''; + const isProduct = serializedMediaData.type === 'product'; - if (window.Store.isForwardMediaWithCaptionsEnabled() && - withCaption && - msg.isCaptionByUser) { - caption = msg.caption; - } + const caption = + (withCaption && (isImageOrVideo || msg.isCaptionByUser)) || + isBtnOrList || isProduct + ? msg.caption + : undefined; const mediaPrep = new window.Store.MediaPrep.MediaPrep( serializedMediaData.type, @@ -392,7 +394,7 @@ exports.LoadUtils = () => { isAvatar: msg.isAvatar !== null && msg.isAvatar !== undefined && msg.isAvatar }; - await mediaPrep.sendToChat(chat, mediaMessageOptions); + return await mediaPrep.sendToChat(chat, mediaMessageOptions); }; window.WWebJS.editMessage = async (msg, content, options = {}) => { From d58ee28f5b397647198d0b639084f2b9f6755646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Thu, 22 Jun 2023 04:46:01 +0300 Subject: [PATCH 12/32] _forwardMessageAndSendToChat simplified for text msgs --- src/util/Injected.js | 45 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 8f683c4aa..1f0e6f797 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -24,6 +24,7 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0]; window.Store.getAsMms = window.mR.findModule('getAsMms')[0].getAsMms; window.Store.getIsSentByMe = window.mR.findModule('getIsSentByMe')[0].getIsSentByMe; + window.Store.getForwardedMessageFields = window.mR.findModule('Chat')[0].getForwardedMessageFields; window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default; window.Store.MessageInfo = window.mR.findModule('sendQueryMsgInfo')[0]; window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default; @@ -294,7 +295,6 @@ exports.LoadUtils = () => { }; window.WWebJS.forwardMessage = async (chat, msg, options = {}) => { - const chatId = chat.id; const contact = chat.contact; if (contact.isUser && contact.isContactBlocked) { @@ -309,7 +309,48 @@ exports.LoadUtils = () => { return await window.WWebJS.forwardMediaMessage(chat, msg, options); } - return await chat._forwardMessageAndSendToChat(msg, chatId, ...Object.values(options)); + const forwardedMsgFields = window.Store.getForwardedMessageFields(msg); + const ephemeralFields = window.Store.EphemeralFields.getEphemeralFields(chat); + const meUser = window.Store.User.getMaybeMeUser(); + const newId = await window.Store.MsgKey.newId(); + const isMD = window.Store.MDBackend; + + const newMsgId = new window.Store.MsgKey({ + from: meUser, + to: chat.id, + id: newId, + participant: isMD && chat.id.isGroup() ? meUser : undefined, + selfDir: 'out', + }); + + if (msg.ctwaContext) { + forwardedMsgFields.body = msg.ctwaContext.sourceUrl; + forwardedMsgFields.type = 'chat'; + forwardedMsgFields.mediaObject = undefined; + } + + Object.assign(forwardedMsgFields, ephemeralFields); + + const newMessage = { + ...forwardedMsgFields, + ...ephemeralFields, + id: newMsgId, + from: meUser, + t: parseInt(new Date().getTime() / 1000), + to: chat.id, + ack: 0, + participant: undefined, + local: true, + self: 'out', + isNewMsg: true, + star: false, + isForwarded: msg.isForwarded || !window.Store.getIsSentByMe(msg), + forwardedFromWeb: true, + forwardingScore: msg.getForwardingScoreWhenForwarded(), + multicast: options.multicast + }; + + await window.Store.SendMessage.addAndSendMsgToChat(chat, newMessage); }; window.WWebJS.forwardMediaMessage = async (chat, msg, options = {}) => { From 025f289f4d5817954198162b39864ffc4c021c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Thu, 29 Jun 2023 01:48:20 +0300 Subject: [PATCH 13/32] code refactoring --- src/structures/Message.js | 8 +++--- src/util/Injected.js | 53 ++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index 0a4477dc7..413106b0a 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -384,15 +384,17 @@ class Message extends Base { */ /** - * Forwards this message to another chat (that you chatted before, otherwise it will fail) + * Forwards this message to another chat * + * @note In order to forward messages with video/animated sticker/gif you have to use Chrome instead of Chromium * @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded * @param {MessageForwardOptions} [options] Options used when forwarding the message * @returns {Promise} */ async forward(chat, options = {}) { const chatId = typeof chat === 'string' ? chat : chat.id._serialized; - let internalOptions = { + + const forwardOptions = { multicast: options.multicast || false, withCaption: options.withCaption === false ? false : true }; @@ -403,7 +405,7 @@ class Message extends Base { const message = window.Store.Msg.get(msgId); return await window.WWebJS.forwardMessage(chat, message, options); - }, chatId, this.id._serialized, internalOptions); + }, chatId, this.id._serialized, forwardOptions); } /** diff --git a/src/util/Injected.js b/src/util/Injected.js index 1f0e6f797..f225429b3 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -298,14 +298,12 @@ exports.LoadUtils = () => { const contact = chat.contact; if (contact.isUser && contact.isContactBlocked) { - throw new Error('Attempted forwarding to a blocked contact', contact); + throw new Error('Attempted forwarding to a blocked contact'); } - const _isMediaMsg = (msg) => { - return Boolean(window.Store.getAsMms(msg) && !msg.ctwaContext); - }; + const isMediaMsg = Boolean(window.Store.getAsMms(msg) && !msg.ctwaContext); - if (_isMediaMsg(msg)) { + if (isMediaMsg) { return await window.WWebJS.forwardMediaMessage(chat, msg, options); } @@ -329,8 +327,6 @@ exports.LoadUtils = () => { forwardedMsgFields.mediaObject = undefined; } - Object.assign(forwardedMsgFields, ephemeralFields); - const newMessage = { ...forwardedMsgFields, ...ephemeralFields, @@ -358,32 +354,32 @@ exports.LoadUtils = () => { const mediaObject = msg.mediaObject; if (!mediaObject) { - throw new Error('Forwarding media message without media object', msg); + throw new Error('Forwarding media message without media object'); } - const serializedMediaData = msg.mediaData.toJSON(); + const mediaData = msg.mediaData.toJSON(); - if (serializedMediaData.preview != null) { - (serializedMediaData.preview = mediaObject.contentInfo._preview); + if (mediaData.preview != null) { + (mediaData.preview = mediaObject.contentInfo._preview); } - if (serializedMediaData.mediaBlob instanceof window.Store.OpaqueData) { - serializedMediaData.mediaBlob.retain(); + if (mediaData.mediaBlob instanceof window.Store.OpaqueData) { + mediaData.mediaBlob.retain(); } const mimetype = { - mimetype: serializedMediaData.mimetype + mimetype: mediaData.mimetype }; - let mediaType; - serializedMediaData.isGif ? - mediaType = { + const mediaType = mediaData.isGif + ? { ...mimetype, isGif: true - } : mediaType = mimetype; + } + : mimetype; - if (serializedMediaData.type === 'ptt') { - serializedMediaData.type = 'audio'; + if (mediaData.type === 'ptt') { + mediaData.type = 'audio'; } const productMsgOptions = { @@ -400,24 +396,25 @@ exports.LoadUtils = () => { }; const isImageOrVideo = - serializedMediaData.type === 'image' || - serializedMediaData.type === 'video'; + mediaData.type === 'image' || + mediaData.type === 'video'; const isBtnOrList = - serializedMediaData.type === 'document' && + mediaData.type === 'document' && (msg.isFromTemplate || msg.isDynamicReplyButtonsMsg); - const isProduct = serializedMediaData.type === 'product'; + const isProduct = mediaData.type === 'product'; const caption = (withCaption && (isImageOrVideo || msg.isCaptionByUser)) || - isBtnOrList || isProduct + isBtnOrList || + isProduct ? msg.caption : undefined; const mediaPrep = new window.Store.MediaPrep.MediaPrep( - serializedMediaData.type, - Promise.resolve(serializedMediaData) + mediaData.type, + Promise.resolve(mediaData) ); const mediaMessageOptions = { @@ -435,7 +432,7 @@ exports.LoadUtils = () => { isAvatar: msg.isAvatar !== null && msg.isAvatar !== undefined && msg.isAvatar }; - return await mediaPrep.sendToChat(chat, mediaMessageOptions); + await mediaPrep.sendToChat(chat, mediaMessageOptions); }; window.WWebJS.editMessage = async (msg, content, options = {}) => { From 9f4ea842e20bd195ff2dba591b540b0dfa0365ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Thu, 29 Jun 2023 01:49:09 +0300 Subject: [PATCH 14/32] example clarification --- example.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/example.js b/example.js index b5197e9fb..e923942d9 100644 --- a/example.js +++ b/example.js @@ -5,7 +5,7 @@ const client = new Client({ // proxyAuthentication: { username: 'username', password: 'password' }, puppeteer: { // args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'], - headless: false + headless: true, } }); @@ -225,17 +225,22 @@ client.on('message', async msg => { } else if (msg.author) { /** - * Note: forwarding with caption is available for media messages (photo/video) - * and messages with attachments (documets) also. + * Note: + * In order to forward messages with video/animated sticker/gif + * you have to use Chrome instead of Chromium with @property {executablePath} + * @see https://github.com/pedroslopez/whatsapp-web.js/pull/2272 + * @see https://pptr.dev/api/puppeteer.configuration * * Let's say the message was sent in a group - * and you want to forward it to its author. + * and you want to forward it to its author: + * + * 1. By default it will be forwarded with a caption text (if provided): */ - - // 1. By default it will be forwarded with a caption text (if provided): await msg.forward(msg.author); - // 2. To forward without a caption text (if provided) do: + /** + * 2. To forward without a caption text use @property {withCaption: false}: + */ await msg.forward(msg.author, { withCaption: false }); } }); From 5dfaf315aea08feaafb9922a9436d6a0af2aefda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Thu, 29 Jun 2023 02:32:52 +0300 Subject: [PATCH 15/32] recommendation to use chrome for full functionality --- example.js | 4 ++-- src/structures/Message.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example.js b/example.js index e923942d9..38da0de56 100644 --- a/example.js +++ b/example.js @@ -226,8 +226,8 @@ client.on('message', async msg => { else if (msg.author) { /** * Note: - * In order to forward messages with video/animated sticker/gif - * you have to use Chrome instead of Chromium with @property {executablePath} + * In order to avoid unexpected behaviour while forwarding media and attachment messages + * you have to use Chrome instead of Chromium by adding @property {executablePath} * @see https://github.com/pedroslopez/whatsapp-web.js/pull/2272 * @see https://pptr.dev/api/puppeteer.configuration * diff --git a/src/structures/Message.js b/src/structures/Message.js index 413106b0a..3f6e916dc 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -386,7 +386,7 @@ class Message extends Base { /** * Forwards this message to another chat * - * @note In order to forward messages with video/animated sticker/gif you have to use Chrome instead of Chromium + * @note In order to avoid unexpected behaviour while forwarding media and attachment messages you have to use Chrome instead of Chromium * @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded * @param {MessageForwardOptions} [options] Options used when forwarding the message * @returns {Promise} From fed8cfbfce033d9b62b8cce502254de5439c4618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Thu, 29 Jun 2023 07:21:04 +0300 Subject: [PATCH 16/32] docs clarification --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 431e9a337..5256d740d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -843,7 +843,7 @@ declare namespace WAWebJS { /** React to this message with an emoji*/ react: (reaction: string) => Promise, /** - * Forwards this message to another chat (that you chatted before, otherwise it will fail) + * Forwards this message to another chat */ forward: (chat: Chat | string, options?: MessageForwardOptions) => Promise, /** Star this message */ From 3f456e8aa687509077aef82c542505da0c209ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AL=CE=9EX?= Date: Mon, 31 Jul 2023 11:53:50 +0300 Subject: [PATCH 17/32] fix: ESLint --- src/Client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client.js b/src/Client.js index e5d45c368..93622dc07 100644 --- a/src/Client.js +++ b/src/Client.js @@ -238,9 +238,9 @@ class Client extends EventEmitter { // Listens to qr token change if (mut.type === 'attributes' && mut.attributeName === 'data-ref') { window.qrChanged(mut.target.dataset.ref); - } else + } // Listens to retry button, when found, click it - if (mut.type === 'childList') { + else if (mut.type === 'childList') { const retry_button = document.querySelector(selectors.QR_RETRY_BUTTON); if (retry_button) retry_button.click(); } From 6d84b349ebccd7f552d9a11dfb20ff67831e242e Mon Sep 17 00:00:00 2001 From: alechkos Date: Sat, 26 Aug 2023 11:17:56 +0300 Subject: [PATCH 18/32] small update to improve the performance of findImpl fix --- src/util/Injected.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 8fdaf111f..88d8a4dcb 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -83,11 +83,8 @@ exports.ExposeStore = (moduleRaidStr) => { }; } - if (window.mR.findModule('ChatCollection')[0] && window.mR.findModule('ChatCollection')[0].ChatCollection) { - if (typeof window.mR.findModule('ChatCollection')[0].ChatCollection.findImpl === 'undefined' && typeof window.mR.findModule('ChatCollection')[0].ChatCollection._find != 'undefined') { - window.mR.findModule('ChatCollection')[0].ChatCollection.findImpl = window.mR.findModule('ChatCollection')[0].ChatCollection._find; - } - } + // eslint-disable-next-line no-undef + if ((m = window.mR.findModule('ChatCollection')[0]) && m.ChatCollection && typeof m.ChatCollection.findImpl === 'undefined' && typeof m.ChatCollection._find !== 'undefined') m.ChatCollection.findImpl = m.ChatCollection._find; // TODO remove these once everybody has been updated to WWebJS with legacy sessions removed const _linkPreview = window.mR.findModule('queryLinkPreview'); From 0d1d4a4afacc8300f7b4f1affbd5626bd03a3f00 Mon Sep 17 00:00:00 2001 From: alechkos Date: Sat, 26 Aug 2023 11:18:21 +0300 Subject: [PATCH 19/32] window.compareWwebVersions fixed --- src/Client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client.js b/src/Client.js index e5806a9a7..adba49b02 100644 --- a/src/Client.js +++ b/src/Client.js @@ -285,13 +285,13 @@ class Client extends EventEmitter { */ window.compareWwebVersions = (lOperand, operator, rOperand) => { if (!['>', '>=', '<', '<=', '='].includes(operator)) { - throw class _ extends Error { + throw new class _ extends Error { constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; } }('Invalid comparison operator is provided'); } if (typeof lOperand !== 'string' || typeof rOperand !== 'string') { - throw class _ extends Error { + throw new class _ extends Error { constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; } }('A non-string WWeb version type is provided'); } From dc9014f72ed1cc7fc9670be6b1ab45279705feb5 Mon Sep 17 00:00:00 2001 From: alechkos Date: Sun, 27 Aug 2023 18:32:04 +0300 Subject: [PATCH 20/32] getForwardedMessageFields fixed --- src/util/Injected.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 88d8a4dcb..22879fcd7 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -24,7 +24,7 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0]; window.Store.getAsMms = window.mR.findModule('getAsMms')[0].getAsMms; window.Store.getIsSentByMe = window.mR.findModule('getIsSentByMe')[0].getIsSentByMe; - window.Store.getForwardedMessageFields = window.mR.findModule('Chat')[0].getForwardedMessageFields; + window.Store.getForwardedMessageFields = window.mR.findModule('getForwardedMessageFields')[0].getForwardedMessageFields; window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default; window.Store.MessageInfo = window.mR.findModule('sendQueryMsgInfo')[0]; window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default; From 780c5e7a989bfe9e4271f904afcae149be3c4b25 Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Wed, 1 Nov 2023 03:09:48 +0200 Subject: [PATCH 21/32] window.injectToFunction clarifications --- src/util/Injected.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 9cbbe9c57..b60a7344f 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -131,9 +131,9 @@ exports.ExposeStore = (moduleRaidStr) => { /** * Target options object description * @typedef {Object} TargetOptions - * @property {string|number} moduleId The name or a key of the target module to search + * @property {string|number} module The name or a key of the target module to search * @property {number} index The index value of the target module - * @property {string} property The function name to get from a module + * @property {string} function The function name to get from a module */ /** @@ -142,17 +142,17 @@ exports.ExposeStore = (moduleRaidStr) => { * @param {Function} callback Modified function */ window.injectToFunction = (target, callback) => { - const module = typeof target.moduleId === 'string' - ? window.mR.findModule(target.moduleId) - : window.mR.modules[target.moduleId]; - const originalFunction = module[target.index][target.property]; + const module = typeof target.module === 'string' + ? window.mR.findModule(target.module) + : window.mR.modules[target.module]; + const originalFunction = module[target.index][target.function]; const modifiedFunction = (...args) => callback(originalFunction, ...args); - module[target.index][target.property] = modifiedFunction; + module[target.index][target.function] = modifiedFunction; }; - window.injectToFunction({ moduleId: 'mediaTypeFromProtobuf', index: 0, property: 'mediaTypeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage ? null : func(...args); }); + window.injectToFunction({ module: 'mediaTypeFromProtobuf', index: 0, function: 'mediaTypeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage ? null : func(...args); }); - window.injectToFunction({ moduleId: 'typeAttributeFromProtobuf', index: 0, property: 'typeAttributeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage || proto.groupInviteMessage ? 'text' : func(...args); }); + window.injectToFunction({ module: 'typeAttributeFromProtobuf', index: 0, function: 'typeAttributeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage || proto.groupInviteMessage ? 'text' : func(...args); }); }; exports.LoadUtils = () => { From 393bd6b982d1a2ea73c9b0cf6289617df5a6dabd Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:32:09 +0200 Subject: [PATCH 22/32] [BUG] fixed Message.forward method --- src/structures/Message.js | 27 +++---- src/util/Injected.js | 154 +++++++++++++++----------------------- 2 files changed, 69 insertions(+), 112 deletions(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index 099189609..59b692678 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -404,36 +404,27 @@ class Message extends Base { } /** - * Forward options: - * + * Message forward options * @typedef {Object} MessageForwardOptions - * @property {boolean} [multicast=false] - * @property {boolean} [withCaption=true] Forwards this message with the caption text of the original message if provided. + * @property {boolean} [withCaption=true] Forwards this message with the caption text of the original message if provided + * @property {?boolean} displayAsForwarded If true, the forwarded message will be displayed as forwarded. If not provided, the value will be as the default behavior */ /** * Forwards this message to another chat - * - * @note In order to avoid unexpected behaviour while forwarding media and attachment messages you have to use Chrome instead of Chromium * @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded * @param {MessageForwardOptions} [options] Options used when forwarding the message - * @returns {Promise} + * @returns {Promise} */ async forward(chat, options = {}) { const chatId = typeof chat === 'string' ? chat : chat.id._serialized; - const forwardOptions = { - multicast: options.multicast || false, - withCaption: options.withCaption === false ? false : true - }; - - await this.client.pupPage.evaluate(async (chatId, msgId, options) => { - const chatWid = window.Store.WidFactory.createWid(chatId); - const chat = await window.Store.Chat.find(chatWid); - const message = window.Store.Msg.get(msgId); + return await this.client.pupPage.evaluate(async (msgId, chatId, options) => { + const chat = window.Store.Chat.get(chatId); + const msg = window.Store.Msg.get(msgId); - return await window.WWebJS.forwardMessage(chat, message, options); - }, chatId, this.id._serialized, forwardOptions); + return await window.WWebJS.forwardMessage(chat, msg, options); + }, this.id._serialized, chatId, options); } /** diff --git a/src/util/Injected.js b/src/util/Injected.js index b60a7344f..915738f87 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -22,9 +22,6 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.NumberInfo = window.mR.findModule('formattedPhoneNumber')[0]; window.Store.MediaTypes = window.mR.findModule('msgToMediaType')[0]; window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0]; - window.Store.getAsMms = window.mR.findModule('getAsMms')[0].getAsMms; - window.Store.getIsSentByMe = window.mR.findModule('getIsSentByMe')[0].getIsSentByMe; - window.Store.getForwardedMessageFields = window.mR.findModule('getForwardedMessageFields')[0].getForwardedMessageFields; window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default; window.Store.MessageInfo = window.mR.findModule('sendQueryMsgInfo')[0]; window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default; @@ -51,7 +48,6 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.ConversationMsgs = window.mR.findModule('loadEarlierMsgs')[0]; window.Store.sendReactionToMsg = window.mR.findModule('sendReactionToMsg')[0].sendReactionToMsg; window.Store.createOrUpdateReactionsModule = window.mR.findModule('createOrUpdateReactions')[0]; - window.Store.EphemeralFields = window.mR.findModule('getEphemeralFields')[0]; window.Store.MsgActionChecks = window.mR.findModule('canSenderRevokeMsg')[0]; window.Store.QuotedMsg = window.mR.findModule('getQuotedMsgObj')[0]; window.Store.Socket = window.mR.findModule('deprecatedSendIq')[0]; @@ -61,6 +57,7 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.LidUtils = window.mR.findModule('getCurrentLid')[0]; window.Store.WidToJid = window.mR.findModule('widToUserJid')[0]; window.Store.JidToWid = window.mR.findModule('userJidToUserWid')[0]; + window.Store.MessageFieldGetters = window.mR.findModule('getShouldDisplayAsForwarded')[0]; window.Store.Settings = { ...window.mR.findModule('ChatlistPanelState')[0], setPushname: window.mR.findModule((m) => m.setPushname && !m.ChatlistPanelState)[0].setPushname @@ -71,6 +68,10 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.ReplyUtils = (m = window.mR.findModule('canReplyMsg')).length > 0 && m[0]; /* eslint-enable no-undef, no-cond-assign */ + window.Store.EphemeralFields = { + ...window.mR.findModule('getEphemeralFields')[0], + ...window.mR.findModule('isEphemeralSettingOn')[0] + }; window.Store.StickerTools = { ...window.mR.findModule('toWebpSticker')[0], ...window.mR.findModule('addWebpMetadata')[0] @@ -97,6 +98,11 @@ exports.ExposeStore = (moduleRaidStr) => { ...window.mR.findModule('getMembershipApprovalRequests')[0], ...window.mR.findModule('sendMembershipRequestsActionRPC')[0] }; + window.Store.ForwardMessageUtils = { + ...window.mR.findModule('getAsMms')[0], + ...window.mR.findModule('getForwardedMessageFields')[0], + ...window.mR.findModule('getNewsletterContextForForwardedMsg')[0] + }; if (!window.Store.Chat._find) { window.Store.Chat._find = e => { @@ -374,41 +380,41 @@ exports.LoadUtils = () => { window.WWebJS.forwardMessage = async (chat, msg, options = {}) => { const contact = chat.contact; - if (contact.isUser && contact.isContactBlocked) { - throw new Error('Attempted forwarding to a blocked contact'); + return ('ForwardMessageError: Attempted forwarding to a blocked contact'); } - - const isMediaMsg = Boolean(window.Store.getAsMms(msg) && !msg.ctwaContext); - + const isMediaMsg = Boolean(window.Store.ForwardMessageUtils.getAsMms(msg) && !msg.ctwaContext); if (isMediaMsg) { - return await window.WWebJS.forwardMediaMessage(chat, msg, options); + const result = await window.WWebJS.forwardMediaMessage(chat, msg, options); + return result.messageSendResult === 'OK'; } - - const forwardedMsgFields = window.Store.getForwardedMessageFields(msg); - const ephemeralFields = window.Store.EphemeralFields.getEphemeralFields(chat); + const forwardedMsgFields = window.Store.ForwardMessageUtils.getForwardedMessageFields(msg); const meUser = window.Store.User.getMaybeMeUser(); + let participant; + chat.isGroup && (participant = window.Store.WidFactory.toUserWid(meUser)); const newId = await window.Store.MsgKey.newId(); const isMD = window.Store.MDBackend; - - const newMsgId = new window.Store.MsgKey({ + const newMsgKey = new window.Store.MsgKey({ from: meUser, to: chat.id, id: newId, - participant: isMD && chat.id.isGroup() ? meUser : undefined, + participant: isMD && participant, selfDir: 'out', }); - if (msg.ctwaContext) { forwardedMsgFields.body = msg.ctwaContext.sourceUrl; forwardedMsgFields.type = 'chat'; - forwardedMsgFields.mediaObject = undefined; + delete forwardedMsgFields.mediaObject; } - + forwardedMsgFields.forwardedNewsletterMessageInfo = window.Store.ForwardMessageUtils.getNewsletterContextForForwardedMsg(msg); + window.Store.EphemeralFields.isEphemeralSettingOn(chat) && (forwardedMsgFields.ephemeralDuration = window.Store.EphemeralFields.getEphemeralSetting(chat)); + forwardedMsgFields.ephemeralSettingTimestamp = window.Store.EphemeralFields.getEphemeralSettingTimestamp(chat); + forwardedMsgFields.disappearingModeInitiator = window.Store.EphemeralFields.getDisappearingModeInitiator(chat); + forwardedMsgFields.disappearingModeTrigger = window.Store.EphemeralFields.getDisappearingModeTrigger(chat); + forwardedMsgFields.disappearingModeInitiatedByMe = window.Store.EphemeralFields.getDisappearingModeInitiatedByMe(chat); const newMessage = { ...forwardedMsgFields, - ...ephemeralFields, - id: newMsgId, + id: newMsgKey, from: meUser, t: parseInt(new Date().getTime() / 1000), to: chat.id, @@ -418,49 +424,24 @@ exports.LoadUtils = () => { self: 'out', isNewMsg: true, star: false, - isForwarded: msg.isForwarded || !window.Store.getIsSentByMe(msg), + isForwarded: options.displayAsForwarded ?? window.Store.MessageFieldGetters.getShouldDisplayAsForwarded(msg), forwardedFromWeb: true, forwardingScore: msg.getForwardingScoreWhenForwarded(), - multicast: options.multicast + multicast: true }; - - await window.Store.SendMessage.addAndSendMsgToChat(chat, newMessage); + const [, result] = await Promise.all(window.Store.SendMessage.addAndSendMsgToChat(chat, newMessage)); + return result.messageSendResult === 'OK'; }; window.WWebJS.forwardMediaMessage = async (chat, msg, options = {}) => { - const { multicast: multicast, withCaption: withCaption } = options; - const mediaObject = msg.mediaObject; - - if (!mediaObject) { - throw new Error('Forwarding media message without media object'); - } - + const { withCaption = true } = options; const mediaData = msg.mediaData.toJSON(); - - if (mediaData.preview != null) { - (mediaData.preview = mediaObject.contentInfo._preview); - } - - if (mediaData.mediaBlob instanceof window.Store.OpaqueData) { - mediaData.mediaBlob.retain(); - } - - const mimetype = { - mimetype: mediaData.mimetype - }; - - const mediaType = mediaData.isGif - ? { - ...mimetype, - isGif: true - } - : mimetype; - - if (mediaData.type === 'ptt') { - mediaData.type = 'audio'; - } - - const productMsgOptions = { + mediaData.preview && (mediaData.preview = msg.mediaObject.contentInfo._preview); + mediaData.mediaBlob instanceof window.Store.OpaqueData && mediaData.mediaBlob.retain(); + let placeholderProps = { mimetype: mediaData.mimetype }; + mediaData.isGif && (placeholderProps = { ...placeholderProps, isGif: true }); + mediaData.type === 'ptt' && (mediaData.type = 'audio'); + const productOptions = { businessOwnerJid: msg.businessOwnerJid, productId: msg.productId, currencyCode: msg.currencyCode, @@ -472,45 +453,30 @@ exports.LoadUtils = () => { title: msg.title, description: msg.description }; - - const isImageOrVideo = - mediaData.type === 'image' || - mediaData.type === 'video'; - - const isBtnOrList = - mediaData.type === 'document' && - (msg.isFromTemplate || msg.isDynamicReplyButtonsMsg); - + const isImageOrVideo = mediaData.type === 'image' || mediaData.type === 'video'; const isProduct = mediaData.type === 'product'; - - const caption = - (withCaption && (isImageOrVideo || msg.isCaptionByUser)) || - isBtnOrList || - isProduct - ? msg.caption - : undefined; - - const mediaPrep = new window.Store.MediaPrep.MediaPrep( - mediaData.type, - Promise.resolve(mediaData) + const caption = (withCaption && (isImageOrVideo || msg.isCaptionByUser)) || isProduct + ? msg.caption + : undefined; + const mediaPrep = new window.Store.MediaPrep.MediaPrep(mediaData.type, Promise.resolve(mediaData)); + return await mediaPrep.sendToChat( + chat, + { + forwardedFromWeb: true, + caption: caption, + mentionedJidList: msg.mentionedJidList, + groupMentions: msg.groupMentions, + footer: isProduct && msg.footer, + addEvenWhilePreparing: true, + placeholderProps: placeholderProps, + isForwarded: options.displayAsForwarded ?? window.Store.MessageFieldGetters.getShouldDisplayAsForwarded(msg), + forwardingScore: msg.getForwardingScoreWhenForwarded(), + multicast: true, + productMsgOptions: productOptions, + isAvatar: msg.isAvatar, + forwardedNewsletterMessageInfo: window.Store.ForwardMessageUtils.getNewsletterContextForForwardedMsg(msg) + } ); - - const mediaMessageOptions = { - forwardedFromWeb: true, - caption: caption, - mentionedJidList: msg.mentionedJidList, - groupMentions: msg.groupMentions, - footer: msg.type === 'product' ? msg.footer : undefined, - addEvenWhilePreparing: true, - placeholderProps: mediaType, - isForwarded: msg.isForwarded || !window.Store.getIsSentByMe(msg), - forwardingScore: msg.getForwardingScoreWhenForwarded(), - multicast: multicast, - productMsgOptions: productMsgOptions, - isAvatar: msg.isAvatar !== null && msg.isAvatar !== undefined && msg.isAvatar - }; - - await mediaPrep.sendToChat(chat, mediaMessageOptions); }; window.WWebJS.editMessage = async (msg, content, options = {}) => { From 4e7af64c12b02a11414214a5a1ad43ad8367e693 Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Fri, 24 Nov 2023 22:27:48 +0200 Subject: [PATCH 23/32] [CHG] usage example updated --- example.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/example.js b/example.js index d75c97229..881adf3e6 100644 --- a/example.js +++ b/example.js @@ -376,23 +376,18 @@ client.on('message', async msg => { } else if (msg.author) { /** - * Note: - * In order to avoid unexpected behaviour while forwarding media and attachment messages - * you have to use Chrome instead of Chromium by adding @property {executablePath} - * @see https://github.com/pedroslopez/whatsapp-web.js/pull/2272 - * @see https://pptr.dev/api/puppeteer.configuration - * * Let's say the message was sent in a group * and you want to forward it to its author: - * - * 1. By default it will be forwarded with a caption text (if provided): */ + + // 1. By default the message will be forwarded with a caption (if provided): await msg.forward(msg.author); - /** - * 2. To forward without a caption text use @property {withCaption: false}: - */ + // 2. To forward without a caption text: await msg.forward(msg.author, { withCaption: false }); + + // 3. To forward without a 'Forwarded' title: + await msg.forward(msg.author, { displayAsForwarded: false }); } }); From 7ce66e23e3ff0e4e50b5896d936523920e5961b1 Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Fri, 24 Nov 2023 22:38:25 +0200 Subject: [PATCH 24/32] [BUG] added missing module --- src/util/Injected.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/Injected.js b/src/util/Injected.js index 349b8ec9e..2a48f5225 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -56,6 +56,7 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.LidUtils = window.mR.findModule('getCurrentLid')[0]; window.Store.WidToJid = window.mR.findModule('widToUserJid')[0]; window.Store.JidToWid = window.mR.findModule('userJidToUserWid')[0]; + window.Store.MessageFieldGetters = window.mR.findModule('getShouldDisplayAsForwarded')[0]; /* eslint-disable no-undef, no-cond-assign */ window.Store.QueryExist = ((m = window.mR.findModule('queryExists')[0]) ? m.queryExists : window.mR.findModule('queryExist')[0].queryWidExists); From 181c5e94afd985a4386dc358e0363ac35cb19ddb Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:37:13 +0200 Subject: [PATCH 25/32] fix: method updated to be compatible with wweb v2.2353.0 --- src/util/Injected.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 0e331918a..cd220ef99 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -388,7 +388,7 @@ exports.LoadUtils = () => { const result = await window.WWebJS.forwardMediaMessage(chat, msg, options); return result.messageSendResult === 'OK'; } - const forwardedMsgFields = window.Store.ForwardMessageUtils.getForwardedMessageFields(msg); + const forwardedMsgFields = window.Store.ForwardMessageUtils.getForwardedMessageFields(msg, chat); const meUser = window.Store.User.getMaybeMeUser(); let participant; chat.isGroup && (participant = window.Store.WidFactory.toUserWid(meUser)); From f06415592b9da0ad5d22e9695cc8718440df080b Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:02:51 +0200 Subject: [PATCH 26/32] fix: typo --- index.d.ts | 2 +- src/Client.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index fa9b3c9cd..755c7b179 100644 --- a/index.d.ts +++ b/index.d.ts @@ -440,7 +440,7 @@ declare namespace WAWebJS { /** User agent to use in puppeteer. * @default 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36' */ userAgent?: string - /** Ffmpeg path to use when formating videos to webp while sending stickers + /** Ffmpeg path to use when formatting videos to webp while sending stickers * @default 'ffmpeg' */ ffmpegPath?: string, /** Object with proxy autentication requirements @default: undefined */ diff --git a/src/Client.js b/src/Client.js index 475067dda..fcb740651 100644 --- a/src/Client.js +++ b/src/Client.js @@ -30,7 +30,7 @@ const NoAuth = require('./authStrategies/NoAuth'); * @param {number} options.takeoverOnConflict - If another whatsapp web session is detected (another browser), take over the session in the current browser * @param {number} options.takeoverTimeoutMs - How much time to wait before taking over the session * @param {string} options.userAgent - User agent to use in puppeteer - * @param {string} options.ffmpegPath - Ffmpeg path to use when formating videos to webp while sending stickers + * @param {string} options.ffmpegPath - Ffmpeg path to use when formatting videos to webp while sending stickers * @param {boolean} options.bypassCSP - Sets bypassing of page's Content-Security-Policy. * @param {object} options.proxyAuthentication - Proxy Authentication object. * From cf2a35686047262f5f90158d3a9a53cf18a10c80 Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Fri, 8 Dec 2023 08:49:25 +0200 Subject: [PATCH 27/32] fix: forward to unknown previously users --- src/structures/Message.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/structures/Message.js b/src/structures/Message.js index d8bd01726..7f355674c 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -420,8 +420,10 @@ class Message extends Base { const chatId = typeof chat === 'string' ? chat : chat.id._serialized; return await this.client.pupPage.evaluate(async (msgId, chatId, options) => { - const chat = window.Store.Chat.get(chatId); + const chatWid = window.Store.WidFactory.createWid(chatId); + const chat = await window.Store.Chat.find(chatWid); const msg = window.Store.Msg.get(msgId); + if (!chat || !msg) return false; return await window.WWebJS.forwardMessage(chat, msg, options); }, this.id._serialized, chatId, options); From b11544d88e33a1679f67f62ef80151a48bfacabf Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Fri, 8 Dec 2023 09:14:32 +0200 Subject: [PATCH 28/32] fix: async function proper handling --- src/Client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.js b/src/Client.js index eb825ab78..47055672f 100644 --- a/src/Client.js +++ b/src/Client.js @@ -892,7 +892,7 @@ class Client extends EventEmitter { if (sendSeen) { - window.WWebJS.sendSeen(chatId); + await window.WWebJS.sendSeen(chatId); } const msg = await window.WWebJS.sendMessage(chat, message, options, sendSeen); From 13d4a7b87d50118be29743e374f8bf513a5597e1 Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Sun, 7 Jan 2024 01:47:59 +0200 Subject: [PATCH 29/32] refactor: revert 'headless: true' --- example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.js b/example.js index cc3ef250a..b01397306 100644 --- a/example.js +++ b/example.js @@ -5,7 +5,7 @@ const client = new Client({ // proxyAuthentication: { username: 'username', password: 'password' }, puppeteer: { // args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'], - headless: true, + headless: false, } }); From b1e2922e3e4739690ad92b02abe10f34999aaf2a Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Sun, 7 Jan 2024 01:55:04 +0200 Subject: [PATCH 30/32] style: fix some minor flaws --- example.js | 2 +- index.d.ts | 4 ++-- src/structures/Message.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example.js b/example.js index b01397306..1c1300ecb 100644 --- a/example.js +++ b/example.js @@ -389,7 +389,7 @@ client.on('message', async msg => { // 2. To forward without a caption text: await msg.forward(msg.author, { withCaption: false }); - // 3. To forward without a 'Forwarded' title: + // 3. To forward without a 'Forwarded' tag: await msg.forward(msg.author, { displayAsForwarded: false }); } }); diff --git a/index.d.ts b/index.d.ts index 34bb84c39..a4c35fc0c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1434,7 +1434,7 @@ declare namespace WAWebJS { message: string; isInviteV4Sent: boolean, } - }; + } /** An object that handles options for adding participants */ export interface AddParticipantsOptions { @@ -1458,7 +1458,7 @@ declare namespace WAWebJS { * @default '' */ comment?: string - }; + } /** An object that handles the information about the group membership request */ export interface GroupMembershipRequest { diff --git a/src/structures/Message.js b/src/structures/Message.js index 7f355674c..3f542cecd 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -407,7 +407,7 @@ class Message extends Base { * Message forward options * @typedef {Object} MessageForwardOptions * @property {boolean} [withCaption=true] Forwards this message with the caption text of the original message if provided - * @property {?boolean} displayAsForwarded If false, the forwarded message will be displayed withour a 'Forwarded' tag, by default the default WhatsApp behavior is used + * @property {boolean} [displayAsForwarded] If false, the forwarded message will be displayed withour a 'Forwarded' tag, by default the default WhatsApp behavior is used */ /** From 355eeaae1401c17b4c058994be80a6c28f6f6989 Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Sun, 7 Jan 2024 01:58:18 +0200 Subject: [PATCH 31/32] fix: correct the check if a user is blocked --- src/util/Injected.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/util/Injected.js b/src/util/Injected.js index 35f979926..0789fd2e0 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -371,10 +371,7 @@ exports.LoadUtils = () => { }; window.WWebJS.forwardMessage = async (chat, msg, options = {}) => { - const contact = chat.contact; - if (contact.isUser && contact.isContactBlocked) { - return ('ForwardMessageError: Attempted forwarding to a blocked contact'); - } + if (chat.isUser && chat.contact.isContactBlocked) return false; const isMediaMsg = Boolean(window.Store.ForwardMessageUtils.getAsMms(msg) && !msg.ctwaContext); if (isMediaMsg) { const result = await window.WWebJS.forwardMediaMessage(chat, msg, options); From ee192c276c5c0768119646f59ec85f9101611627 Mon Sep 17 00:00:00 2001 From: alechkos <93551621+alechkos@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:37:20 +0300 Subject: [PATCH 32/32] style: fix broken link in readme file --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0262a7d94..ba4b9a701 100644 --- a/README.md +++ b/README.md @@ -176,10 +176,10 @@ limitations under the License. [nodejs]: https://nodejs.org/en/download/ [examples]: https://github.com/pedroslopez/whatsapp-web.js/blob/master/example.js [auth-strategies]: https://wwebjs.dev/guide/creating-your-bot/authentication.html -[google-chrome]: https://wwebjs.dev/guide/handling-attachments.html#caveat-for-sending-videos-and-gifs +[google-chrome]: https://wwebjs.dev/guide/creating-your-bot/handling-attachments.html#caveat-for-sending-videos-and-gifs [deprecated-video]: https://www.youtube.com/watch?v=hv1R1rLeVVE [gitHub-sponsors]: https://github.com/sponsors/pedroslopez [support-payPal]: https://www.paypal.me/psla/ [digitalocean]: https://m.do.co/c/73f906a36ed4 [contributing]: https://github.com/pedroslopez/whatsapp-web.js/blob/main/CODE_OF_CONDUCT.md -[whatsapp]: https://whatsapp.com \ No newline at end of file +[whatsapp]: https://whatsapp.com