Compare commits

...

58 Commits
main ... WaWJS2

Author SHA1 Message Date
14f7854bb8 Update Injected.js 2023-02-27 04:53:34 -06:00
22720038b5 Update package.json 2023-02-27 00:06:45 -06:00
83560a2b56 regresamos a version anterios - botones normales 2023-02-21 05:02:01 -06:00
bf93c6a10c Update package.json 2023-02-21 04:50:51 -06:00
d7d7df4b74 Merge branch 'WaWJS2' of https://github.com/cheveguerra/whatsapp-web.js into WaWJS2 2023-02-21 04:47:45 -06:00
7f62f2b62b Update package.json 2023-02-21 03:46:44 -06:00
b4c2915334 Update package.json 2023-02-21 03:45:13 -06:00
Rajeh Taher
e3442e5748 space 2023-02-21 03:34:12 -06:00
Rajeh Taher
d03c1a65c2 ESLint has dyslexia so spacing is important 2023-02-21 03:34:12 -06:00
Rajeh Taher
9cb90ff457 Spacing & undefined variables 2023-02-21 03:34:12 -06:00
Rajeh Taher
9c6b1ab7b1 Final fixes 2023-02-21 03:34:12 -06:00
Rajeh Taher
70b32705c9 missing semicolon 2023-02-21 03:33:46 -06:00
Rajeh Taher
ceabbd5177 ESLINT + Clean 2023-02-21 03:33:46 -06:00
Rajeh Taher
ec0ee3c243 Release all limiters 2023-02-21 03:33:46 -06:00
Rajeh Taher
2ce2c7e97d Basic changes; no real change 2023-02-21 03:33:46 -06:00
Rajeh Taher
eafdfe12f2 space 2023-02-21 01:13:43 +02:00
Rajeh Taher
d126c35e1d ESLint has dyslexia so spacing is important 2023-02-21 00:43:12 +02:00
Rajeh Taher
8a84b5388d Spacing & undefined variables 2023-02-21 00:36:35 +02:00
Rajeh Taher
e810b5146d Final fixes 2023-02-21 00:04:51 +02:00
Rajeh Taher
c0f9e78f6c missing semicolon 2023-02-19 15:47:07 +02:00
Rajeh Taher
7e3b2f6bde ESLINT + Clean 2023-02-19 15:16:29 +02:00
Rajeh Taher
f655453ca4 Release all limiters 2023-02-19 05:29:11 +02:00
Rajeh Taher
2c084ef49e Basic changes; no real change 2023-02-19 05:22:36 +02:00
tuyuribr
74a02cb736 Merge branch 'main' into fix-buttons-list 2023-02-18 10:45:47 -06:00
Rajeh Taher
9e55166164 Merge branch 'main' into fix-buttons-list 2023-02-12 18:08:23 +02:00
29b61b396b Merge branch 'pedroslopez:main' into WaWJS2 2023-02-10 13:18:04 -06:00
tuyuribr
8ae50ec99c Merge branch 'main' into fix-buttons-list 2023-01-29 20:47:15 -03:00
Rajeh Taher
2d6c73e010 Merge branch 'main' into fix-buttons-list 2023-01-24 19:47:49 +02:00
025f9914c3 Merge branch 'pedroslopez:main' into test2 2023-01-15 17:19:13 -06:00
Rajeh Taher
8a45a726ec Merge branch 'main' into fix-buttons-list 2023-01-06 20:17:02 +02:00
Rajeh Taher
d1b2df9051 Merge branch 'main' into fix-buttons-list 2022-12-20 21:48:35 +02:00
Rajeh Taher
c8fe80635a Merge branch 'main' into fix-buttons-list 2022-11-13 23:01:11 +02:00
Rajeh Taher
447c3c2a0f Merge branch 'main' into fix-buttons-list 2022-10-28 01:31:19 +03:00
Rajeh Taher
23d71e9f3b Update Buttons.js 2022-10-22 22:01:37 +03:00
Rajeh Taher
60fbd03256 Merge branch 'main' into fix-buttons-list 2022-10-22 21:34:23 +03:00
Rajeh Taher
d61a1c17d5 Merge branch 'main' into fix-buttons-list 2022-10-10 01:58:05 +03:00
tuyuribr
ceb83cbdec Merge branch 'main' into fix-buttons-list 2022-09-27 12:41:15 -03:00
purpshell
9bfea7847e types 2022-08-23 20:54:01 +03:00
purpshell
045e7bd414 Merge branch 'fix-buttons-list' of https://github.com/pedroslopez/whatsapp-web.js into fix-buttons-list 2022-08-23 20:47:22 +03:00
purpshell
4c72c83dcd example update 2022-08-23 20:43:36 +03:00
purpshell
dfb862614c Types, disabling of templates and example update 2022-08-23 20:43:15 +03:00
Rajeh Taher
9184eca944 Merge branch 'main' into fix-buttons-list 2022-08-18 20:27:04 +03:00
Sergio Carvalho
617ea37b71 Fix buttons list (#1656)
* fix: change the individual buttons limti to 3, still 5 on total

* docs: Change buttons example to include clipboard
2022-08-15 21:04:12 +03:00
Shir Serlui
aba0f3c3c9 Fix buttons sending (#1655)
* Fix buttons sending

* Quick reply id must be a string

* Update src/util/Injected.js

* Update src/structures/Buttons.js

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>
2022-08-15 18:53:28 +03:00
Rajeh Taher
48c8a498fa Merge branch 'main' into fix-buttons-list 2022-08-15 17:21:44 +03:00
Shir Serlui
c32348d3cc Use quick reply (#1651)
* Use template only if url or call buttons exist

* fix typo

* fix for tests

* fix for test

* Update src/util/Injected.js

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* Add quick reply models

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>
2022-08-15 16:11:29 +03:00
Shir Serlui
4e324b140f Use JSON parse and stringify instead of serialize (#1653)
When puppeteer try to return serialized message with buttons, we get undefined instead of the message object, because the message serialized is not a JSON compatible (because of the buttons class)
2022-08-15 12:08:32 +03:00
Shir Serlui
4fe001f32f List work now for bussines accounts (#1652) 2022-08-15 12:03:10 +03:00
purpshell
9ae8f44abc Cleanup 2022-08-15 00:26:00 +03:00
purpshell
6de1ede32b Fixes, and cleanup 2022-08-15 00:16:55 +03:00
Rajeh Taher
19273a434e Merge branch 'main' into fix-buttons-list 2022-08-14 22:36:50 +03:00
Sergio Carvalho
ef68f2f156 Fix buttons list (#1649)
* DOCS: Buttons usage

* FIX: Limit buttons amount to avoid crashes

* STYLE: Fix for ESLINT test
2022-08-14 22:12:01 +03:00
purpshell
627b245143 Merge branch 'fix-buttons-list' of https://github.com/pedroslopez/whatsapp-web.js into fix-buttons-list 2022-08-10 18:11:52 +03:00
purpshell
aee6f057c1 a 2022-08-10 18:11:44 +03:00
Rajeh Taher
383870184f Merge branch 'main' into fix-buttons-list 2022-08-10 17:52:51 +03:00
Rajeh Taher
6b98de9f4f Merge branch 'main' into fix-buttons-list 2022-08-10 13:55:24 +03:00
purpshell
81111faa05 attempted fix. Buttons are broken, lists are working 2022-08-10 13:54:27 +03:00
Rajeh Taher
b1693b49e0 merge (#1635)
* updating forward documentation. (#1624)

* updating forward documentation.

* Update Message.js

* Update index.d.ts

* Update docs/Message.html

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* fix: `star` Error: Evaluation failed: TypeError: msg.chat.sendStarMsgs is not a function (#1598)

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* Update User agent (#1470)

I encountered errors because of this (it says that the chrome version needs to be updated)

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* feat: [Updated] Loading screen listener with percent and message (#1563)

* last update

* eslint fix

* headless fix

* Update index.d.ts

Co-authored-by: stefanfuchs <stefan1234@gmail.com>

* Update index.d.ts - Add 'LOADING_SCREEN' type to Enum

Co-authored-by: stefanfuchs <stefan1234@gmail.com>
Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* feat: Adding file size by bytes to MessageMedia (#1273)

* Update index.d.ts

* Update Message.js

* Update Message.js

* Update MessageMedia.js

* Update MessageMedia.js

* Fix: Cannot read properties of undefined (reading 'id') (#1604)

This change fix `react` evaluation:

```
Error: Evaluation failed: TypeError: Cannot read properties of undefined (reading 'id')
    at Object.<anonymous> (https://web.whatsapp.com/bootstrap_main.44dc3fdf06d9bb8b053d.js:2:103021)
    at Generator.next (<anonymous>)
    at t (https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66483)
    at s (https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66694)
    at https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66753
    at Y (https://web.whatsapp.com/bootstrap_qr.f74b98c729dd38392a5f.js:37:128505)
    at new y (https://web.whatsapp.com/bootstrap_qr.f74b98c729dd38392a5f.js:37:121072)
    at Object.<anonymous> (https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66634)
    at Object.k (https://web.whatsapp.com/bootstrap_main.44dc3fdf06d9bb8b053d.js:2:105511)
    at Object.t.sendReactionToMsg (https://web.whatsapp.com/bootstrap_main.44dc3fdf06d9bb8b053d.js:2:102647)
    at ExecutionContext._evaluateInternal (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:221:19)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async ExecutionContext.evaluate (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:110:16)
    at async Message.react (/app/node_modules/whatsapp-web.js/src/structures/Message.js:344:9)
```

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* Feat: add message_reaction event (#1619)

* Add 'message_reaction' event

Co-authored-by: Nowbie S <33182389+NowDev@users.noreply.github.com>
Co-authored-by: Ruvian S <12111730+matricce@users.noreply.github.com>
Co-authored-by: Yehuda Eisenberg <32451776+YehudaEi@users.noreply.github.com>
Co-authored-by: tonbotfy <106827778+tonbotfy@users.noreply.github.com>
Co-authored-by: stefanfuchs <stefan1234@gmail.com>
Co-authored-by: Jeremy Andes <73316325+jeremyandes@users.noreply.github.com>
Co-authored-by: Wictor Nogueira <57378387+wictornogueira@users.noreply.github.com>
2022-08-10 13:46:16 +03:00
7 changed files with 467 additions and 57 deletions

View File

@@ -32,6 +32,12 @@ client.on('ready', () => {
client.on('message', async msg => { client.on('message', async msg => {
console.log('MESSAGE RECEIVED', msg); console.log('MESSAGE RECEIVED', msg);
if (msg.selectedButtonId == 'test') {
return msg.reply('You clicked the button!');
} else if (msg.selectedRowId == 'test') {
return msg.reply('You clicked that section');
}
if (msg.body === '!ping reply') { if (msg.body === '!ping reply') {
// Send a new message as a reply to the current one // Send a new message as a reply to the current one
msg.reply('pong'); msg.reply('pong');
@@ -189,14 +195,30 @@ client.on('message', async msg => {
client.interface.openChatWindowAt(quotedMsg.id._serialized); client.interface.openChatWindowAt(quotedMsg.id._serialized);
} }
} else if (msg.body === '!buttons') { } else if (msg.body === '!buttons') {
let button = new Buttons('Button body',[{body:'bt1'},{body:'bt2'},{body:'bt3'}],'title','footer'); let button = new Buttons(
'Button body\n\nWant to test buttons some more? Check out https://github.com/wwebjs/buttons-test',
[
{ body: 'Some text' },
{ body: 'Try clicking me (id:test)', id: 'test'},
],
'title',
'footer'
);
client.sendMessage(msg.from, button); client.sendMessage(msg.from, button);
} else if (msg.body === '!list') { } else if (msg.body === '!list') {
let sections = [{title:'sectionTitle',rows:[{title:'ListItem1', description: 'desc'},{title:'ListItem2'}]}]; let sections = [
let list = new List('List body','btnText',sections,'Title','footer'); {
client.sendMessage(msg.from, list); title: 'Secton title',
rows: [
{title:'ListItem1', description: 'desc'},
{title: 'Try clicking me (id: test)', id: 'test'}
]
}
];
let list = new List('List body', 'btnText', sections, 'Custom title', 'custom footer, google.com');
await client.sendMessage(msg.from, list);
} else if (msg.body === '!reaction') { } else if (msg.body === '!reaction') {
msg.react('👍'); await msg.react('👍');
} }
}); });

10
index.d.ts vendored
View File

@@ -1,6 +1,8 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { RequestInit } from 'node-fetch' import { RequestInit } from 'node-fetch'
import { ButtonSpec, FormattedButtonSpec } from './src/structures/Buttons'
import { FormattedSectionSpec, SectionSpec } from './src/structures/List'
import * as puppeteer from 'puppeteer' import * as puppeteer from 'puppeteer'
declare namespace WAWebJS { declare namespace WAWebJS {
@@ -1336,21 +1338,21 @@ declare namespace WAWebJS {
export class List { export class List {
body: string body: string
buttonText: string buttonText: string
sections: Array<any> sections: Array<FormattedSectionSpec>
title?: string | null title?: string | null
footer?: string | null footer?: string | null
constructor(body: string, buttonText: string, sections: Array<any>, title?: string | null, footer?: string | null) constructor(body: string, buttonText: string, sections: Array<SectionSpec>, title?: string | null, footer?: string | null)
} }
/** Message type Buttons */ /** Message type Buttons */
export class Buttons { export class Buttons {
body: string | MessageMedia body: string | MessageMedia
buttons: Array<{ buttonId: string; buttonText: {displayText: string}; type: number }> buttons: FormattedButtonSpec
title?: string | null title?: string | null
footer?: string | null footer?: string | null
constructor(body: string, buttons: Array<{ id?: string; body: string }>, title?: string | null, footer?: string | null) constructor(body: string, buttons: Array<ButtonSpec>, title?: string | null, footer?: string | null)
} }
/** Message type Reaction */ /** Message type Reaction */

View File

@@ -1,7 +1,7 @@
{ {
"name": "whatsapp-web.js", "name": "whatsapp-web.js",
"version": "1.19.4", "version": "1.19.4",
"description": "Library for interacting with the WhatsApp Web API ", "description": "Library for interacting with the WhatsApp Web API - WaWJS2",
"main": "./index.js", "main": "./index.js",
"typings": "./index.d.ts", "typings": "./index.d.ts",
"scripts": { "scripts": {

View File

@@ -693,7 +693,7 @@ class Client extends EventEmitter {
} }
const msg = await window.WWebJS.sendMessage(chat, message, options, sendSeen); const msg = await window.WWebJS.sendMessage(chat, message, options, sendSeen);
return msg.serialize(); return JSON.parse(JSON.stringify(msg));
}, chatId, content, internalOptions, sendSeen); }, chatId, content, internalOptions, sendSeen);
return new Message(this, newMessage); return new Message(this, newMessage);
@@ -937,7 +937,7 @@ class Client extends EventEmitter {
unmuteDate = unmuteDate ? unmuteDate.getTime() / 1000 : -1; unmuteDate = unmuteDate ? unmuteDate.getTime() / 1000 : -1;
await this.pupPage.evaluate(async (chatId, timestamp) => { await this.pupPage.evaluate(async (chatId, timestamp) => {
let chat = await window.Store.Chat.get(chatId); let chat = await window.Store.Chat.get(chatId);
await chat.mute.mute({expiration: timestamp, sendDevice:!0}); await chat.mute.mute(timestamp, !0);
}, chatId, unmuteDate || -1); }, chatId, unmuteDate || -1);
} }

View File

@@ -1,20 +1,22 @@
'use strict'; 'use strict';
const MessageMedia = require('./MessageMedia'); const MessageMedia = require('./MessageMedia');
const Util = require('../util/Util');
/** /**
* Button spec used in Buttons constructor * Button spec used in Buttons constructor
* @typedef {Object} ButtonSpec * @typedef {Object} ButtonSpec
* @property {string=} id - Custom ID to set on the button. A random one will be generated if one is not passed.
* @property {string} body - The text to show on the button. * @property {string} body - The text to show on the button.
* @property {string=} id - Custom ID to set on the button. A random one will be generated if one is not passed.
* @ property {string=} url - Custom URL to set on the button. Optional and will change the type of the button
* @ property {string=} number - Custom URL to set on the button. Optional and will change the type of the button
*/ */
/** /**
* @typedef {Object} FormattedButtonSpec * @typedef {Object} FormattedButtonSpec
* @property {string} buttonId * @property {number} index
* @property {number} type * @property {{displayText: string, url: string}=} urlButton
* @property {Object} buttonText * @property {{displayText: string, phoneNumber: string}=} callButton
* @property {{displayText: string, id: string}=} quickReplyButton
*/ */
/** /**
@@ -66,14 +68,43 @@ class Buttons {
* Creates button array from simple array * Creates button array from simple array
* @param {ButtonSpec[]} buttons * @param {ButtonSpec[]} buttons
* @returns {FormattedButtonSpec[]} * @returns {FormattedButtonSpec[]}
* @example
* Input: [{id:'customId',body:'button1'},{body:'button2'},{body:'button3'},{body:'button4'}]
* Returns: [{ buttonId:'customId',buttonText:{'displayText':'button1'},type: 1 },{buttonId:'n3XKsL',buttonText:{'displayText':'button2'},type:1},{buttonId:'NDJk0a',buttonText:{'displayText':'button3'},type:1}]
*/ */
_format(buttons){ _format(buttons){
buttons = buttons.slice(0,3); // phone users can only see 3 buttons, so lets limit this // Limit the buttons (max 3 of regular and 3 of special buttons) 5 buttons total at the same time
return buttons.map((btn) => { const templateButtons = buttons.filter(button => button.url || button.number).slice(0,3);
return {'buttonId':btn.id ? String(btn.id) : Util.generateHash(6),'buttonText':{'displayText':btn.body},'type':1}; const regularButtons = buttons.filter(button => !button.url && !button.number).slice(0,3);
buttons = templateButtons.concat(regularButtons).slice(0,5);
return buttons.map((button, index) => {
if (button.url && button.number && button.id) throw 'Only pick one of the following (url/number/id)';
if (button.number) {
throw 'Not supported, URL and Call buttons are not supported on IOS';
/* return {
index,
callButton: {
displayText: button.body,
phoneNumber: button.number || ''
}
}; */
} else if (button.url) {
throw 'Not supported, URL and Call buttons are not supported on IOS';
/* return {
index,
urlButton: {
displayText: button.body,
url: button.url || ''
}
}; */
} else {
return {
index,
quickReplyButton: {
displayText: button.body,
id: button.id || `${index}`
}
};
}
}); });
} }

View File

@@ -2,16 +2,38 @@
const Util = require('../util/Util'); const Util = require('../util/Util');
/**
* Section spec used in List constructor
* @typedef {Object} SectionSpec
* @property {string=} title - The title of the section, can be empty on the first section only.
* @property {RowSpec[]} rows - The rows of the section.
*/
/**
* Row spec used in List constructor
* @typedef {Object} RowSpec
* @property {string} title - The text to show on the row.
* @property {string=} id - Custom ID to set on the row. A random one will be generated if one is not passed.
* @property {string=} description - Custom description for the row, will appear after clicked in the list response message (appended)
*/
/**
* Formatted section spec
* @typedef {Object} FormattedSectionSpec
* @property {string} title
* @property {{rowId: string; title: string; description: string}[]} rows
*/
/** /**
* Message type List * Message type List
*/ */
class List { class List {
/** /**
* @param {string} body * @param {string} body - A text body, no media.
* @param {string} buttonText * @param {string} buttonText - The text to put on the click to open button.
* @param {Array<any>} sections * @param {Array<SectionSpec>} sections - The sections of the list
* @param {string?} title * @param {string?} title - Custom boldfaced title property
* @param {string?} footer * @param {string?} footer - Custom footer added in a small font to the end of the message
*/ */
constructor(body, buttonText, sections, title, footer) { constructor(body, buttonText, sections, title, footer) {
/** /**
@@ -49,23 +71,28 @@ class List {
/** /**
* Creates section array from simple array * Creates section array from simple array
* @param {Array<any>} sections * @param {Array<SectionSpec>} sections
* @returns {Array<any>} * @returns {Array<FormattedSectionSpec>}
* @example
* Input: [{title:'sectionTitle',rows:[{id:'customId', title:'ListItem2', description: 'desc'},{title:'ListItem2'}]}}]
* Returns: [{'title':'sectionTitle','rows':[{'rowId':'customId','title':'ListItem1','description':'desc'},{'rowId':'oGSRoD','title':'ListItem2','description':''}]}]
*/ */
_format(sections){ _format(sections) {
if(!sections.length){throw '[LT02] List without sections';} if(!sections.length) {
if(sections.length > 1 && sections.filter(s => typeof s.title == 'undefined').length > 1){throw '[LT05] You can\'t have more than one empty title.';} throw '[LT02] List without sections';
return sections.map( (section) =>{ }
if(!section.rows.length){throw '[LT03] Section without rows';} if(sections.length > 1 && sections.filter(section => (typeof section.title == 'undefined' )|| section.title == '' ).length > 1) {
throw '[LT05] You can\'t have more than one empty title.';
}
return sections.map((section, index) => {
if(!section.rows.length) {
throw '[LT03] Section without rows';
}
return { return {
title: section.title ? section.title : undefined, title: section.title ? section.title : undefined,
rows: section.rows.map( (row) => { rows: section.rows.map((row, rowIndex) => {
if(!row.title){throw '[LT04] Row without title';} if (!row.title) {
throw `[LT04] Row without title at section index ${index} and row index ${rowIndex}`;
}
return { return {
rowId: row.id ? row.id : Util.generateHash(6), rowId: row.id ? row.id : Util.generateHash(8),
title: row.title, title: row.title,
description: row.description ? row.description : '' description: row.description ? row.description : ''
}; };

View File

@@ -74,6 +74,275 @@ exports.ExposeStore = (moduleRaidStr) => {
}; };
} }
// The following was implemented and inspired from wppconnect/wa-js at
// https://github.com/wppconnect-team/wa-js/tree/main/src/chat/functions/prepareMessageButtons.ts
// Find proxy modules
window.findProxyModel = (name) => {
const baseName = name.replace(/Model$/, '');
const names = [baseName];
// ChatModel => "chat"
names.push(baseName.replace(/^(\w)/, (l) => l.toLowerCase()));
// CartItemModel => "cart-item"
// ProductListModel => "product_list"
const parts = baseName.split(/(?=[A-Z])/);
names.push(parts.join('-').toLowerCase());
names.push(parts.join('_').toLowerCase());
const results = window.mR.findModule((m) =>
names.includes(
m.default?.prototype?.proxyName ||
m[name]?.prototype?.proxyName ||
m[baseName]?.prototype?.proxyName
)
)[0];
return results.default || results[name] || results[baseName];
};
// Function to modify functions.
window.injectToFunction = (selector, callback) => {
const oldFunct = window.mR.findModule(selector.name)[selector.index][selector.property];
window.mR.findModule(selector.name)[selector.index][selector.property] = (...args) => callback(oldFunct, args);
};
// Find button models
window.Store.TemplateButtonModel = window.findProxyModel('TemplateButtonModel');
window.Store.TemplateButtonCollection = window.mR.findModule('TemplateButtonCollection')[0].TemplateButtonCollection;
// Find quick reply models
window.Store.ReplyButtonModel = window.findProxyModel('ReplyButtonModel');
window.Store.ButtonCollection = window.mR.findModule('ButtonCollection')[0].ButtonCollection;
// Modify functions
window.injectToFunction({
index: 0,
name: 'createMsgProtobuf',
property: 'createMsgProtobuf'
}, (func, args) => {
const [message] = args;
const proto = func(...args);
if (message.hydratedButtons) {
const hydratedTemplate = {
hydratedButtons: message.hydratedButtons,
};
if (message.footer) {
hydratedTemplate.hydratedFooterText = message.footer;
}
if (message.caption) {
hydratedTemplate.hydratedContentText = message.caption;
}
if (message.title) {
hydratedTemplate.hydratedTitleText = message.title;
}
if (proto.conversation) {
hydratedTemplate.hydratedContentText = proto.conversation;
delete proto.conversation;
} else if (proto.extendedTextMessage?.text) {
hydratedTemplate.hydratedContentText = proto.extendedTextMessage.text;
delete proto.extendedTextMessage;
} else {
// Search media part in message
let found;
const mediaPart = [
'documentMessage',
'imageMessage',
'locationMessage',
'videoMessage',
];
for (const part of mediaPart) {
if (part in proto) {
found = part;
break;
}
}
if (!found) {
return proto;
}
// Media message doesn't allow title
hydratedTemplate[found] = proto[found];
// Copy title to caption if not setted
if (
hydratedTemplate.hydratedTitleText &&
!hydratedTemplate.hydratedContentText
) {
hydratedTemplate.hydratedContentText =
hydratedTemplate.hydratedTitleText;
}
// Remove title for media messages
delete hydratedTemplate.hydratedTitleText;
if (found === 'locationMessage') {
if (
!hydratedTemplate.hydratedContentText &&
(message[found].name || message[found].address)
) {
hydratedTemplate.hydratedContentText =
message[found].name && message[found].address
? `${message[found].name}\n${message[found].address}`
: message[found].name || message[found].address || '';
}
}
// Ensure a content text;
hydratedTemplate.hydratedContentText =
hydratedTemplate.hydratedContentText || ' ';
delete proto[found];
}
proto.templateMessage = {
hydratedTemplate,
};
}
return proto;
});
window.injectToFunction({
index: 0,
name: 'createMsgProtobuf',
property: 'createMsgProtobuf'
}, (func, args) => {
const proto = func(...args);
if (proto.templateMessage) {
proto.viewOnceMessage = {
message: {
templateMessage: proto.templateMessage,
},
};
delete proto.templateMessage;
}
if (proto.buttonsMessage) {
proto.viewOnceMessage = {
message: {
buttonsMessage: proto.buttonsMessage,
},
};
delete proto.buttonsMessage;
}
if (proto.listMessage) {
proto.viewOnceMessage = {
message: {
listMessage: proto.listMessage,
},
};
delete proto.listMessage;
}
return proto;
});
window.injectToFunction({
index: 0,
name: 'typeAttributeFromProtobuf',
property: 'typeAttributeFromProtobuf'
}, (func, args) => {
const [proto] = args;
if (proto.templateMessage?.hydratedTemplate) {
const keys = Object.keys(proto.templateMessage?.hydratedTemplate);
const messagePart = [
'documentMessage',
'imageMessage',
'locationMessage',
'videoMessage',
];
if (messagePart.some((part) => keys.includes(part))) {
return 'media';
}
return 'text';
}
if (
proto.buttonsMessage?.headerType === 1 ||
proto.buttonsMessage?.headerType === 2
) {
return 'text';
}
return func(...args);
});
window.injectToFunction({
index: 0,
name: 'typeAttributeFromProtobuf',
property: 'typeAttributeFromProtobuf'
}, (func, args) => {
const [proto] = args;
if (proto.ephemeralMessage) {
const { message } = proto.ephemeralMessage;
return message ? func(message) : 'text';
}
if (proto.deviceSentMessage) {
const { message } = proto.deviceSentMessage;
return message ? func(message) : 'text';
}
if (proto.viewOnceMessage) {
const { message } = proto.viewOnceMessage;
return message ? func(message) : 'text';
}
return func(...args);
});
window.injectToFunction({
index: 0,
name: 'mediaTypeFromProtobuf',
property: 'mediaTypeFromProtobuf'
}, (func, args) => {
const [proto] = args;
if (proto.templateMessage?.hydratedTemplate) {
return func(proto.templateMessage.hydratedTemplate);
}
return func(...args);
});
window.injectToFunction({
index: 0,
name: 'mediaTypeFromProtobuf',
property: 'mediaTypeFromProtobuf'
}, (func, args) => {
const [proto] = args;
if (proto.deviceSentMessage) {
const { message } = proto.deviceSentMessage;
return message ? func(message) : null;
}
if (proto.ephemeralMessage) {
const { message } = proto.ephemeralMessage;
return message ? func(message) : null;
}
if (proto.viewOnceMessage) {
const { message } = proto.viewOnceMessage;
return message ? func(message) : null;
}
return func(...args);
});
window.injectToFunction({
index: 0,
name: 'encodeMaybeMediaType',
property: 'encodeMaybeMediaType',
}, (func, args) => {
const [type] = args;
if (type === 'button') {
return window.mR.findModule('DROP_ATTR')[0].DROP_ATTR;
}
return func(...args);
});
// TODO remove these once everybody has been updated to WWebJS with legacy sessions removed // TODO remove these once everybody has been updated to WWebJS with legacy sessions removed
const _linkPreview = window.mR.findModule('queryLinkPreview'); const _linkPreview = window.mR.findModule('queryLinkPreview');
if (_linkPreview && _linkPreview[0] && _linkPreview[0].default) { if (_linkPreview && _linkPreview[0] && _linkPreview[0].default) {
@@ -106,6 +375,72 @@ exports.LoadUtils = () => {
}; };
window.WWebJS.prepareMessageButtons = (buttonsOptions) => {
const returnObject = {};
if (!buttonsOptions.buttons) {
return returnObject;
}
if (typeof buttonsOptions.useTemplateButtons === 'undefined' || buttonsOptions.useTemplateButtons === null) {
buttonsOptions.useTemplateButtons = buttonsOptions.buttons.some((button) => {
return 'callButton' in button || 'urlButton' in button;
});
}
returnObject.title = buttonsOptions.title;
returnObject.footer = buttonsOptions.footer;
if (buttonsOptions.useTemplateButtons) {
returnObject.isFromTemplate = true;
returnObject.hydratedButtons = buttonsOptions.buttons;
returnObject.buttons = new window.Store.TemplateButtonCollection;
returnObject.buttons.add(returnObject.hydratedButtons.map((button, index) => {
const buttonIndex = button.index ? button.index : index;
if (button.urlButton) {
return new window.Store.TemplateButtonModel({
id: buttonIndex,
displayText: button.urlButton?.displayText || '',
url: button.urlButton?.url,
subtype: 'url'
});
} else if (button.callButton) {
return new window.Store.TemplateButtonModel({
id: buttonIndex,
displayText: button.callButton?.displayText,
phoneNumber: button.callButton?.phoneNumber,
subtype: 'call'
});
} else {
return new window.Store.TemplateButtonModel({
id: buttonIndex,
displayText: button.quickReplyButton?.displayText,
selectionId: button.quickReplyButton?.id,
subtype: 'quick_reply'
});
}
}));
}
else {
returnObject.isDynamicReplyButtonsMsg = true;
returnObject.dynamicReplyButtons = buttonsOptions.buttons.map((button, index) => ({
buttonId: button.quickReplyButton.id.toString() || `${index}`,
buttonText: {displayText: button.quickReplyButton?.displayText},
type: 1,
}));
// For UI only
returnObject.replyButtons = new window.Store.ButtonCollection();
returnObject.replyButtons.add(returnObject.dynamicReplyButtons.map((button) => new window.Store.ReplyButtonModel({
id: button.buttonId,
displayText: button.buttonText?.displayText || undefined,
})));
}
return returnObject;
};
window.WWebJS.sendMessage = async (chat, content, options = {}) => { window.WWebJS.sendMessage = async (chat, content, options = {}) => {
let attOptions = {}; let attOptions = {};
if (options.attachment) { if (options.attachment) {
@@ -189,7 +524,7 @@ exports.LoadUtils = () => {
delete options.linkPreview; delete options.linkPreview;
// Not supported yet by WhatsApp Web on MD // Not supported yet by WhatsApp Web on MD
if(!window.Store.MDBackend) { if (!window.Store.MDBackend) {
const link = window.Store.Validators.findLink(content); const link = window.Store.Validators.findLink(content);
if (link) { if (link) {
const preview = await window.Store.Wap.queryLinkPreview(link.url); const preview = await window.Store.Wap.queryLinkPreview(link.url);
@@ -201,7 +536,7 @@ exports.LoadUtils = () => {
} }
let buttonOptions = {}; let buttonOptions = {};
if(options.buttons){ if (options.buttons) {
let caption; let caption;
if (options.buttons.type === 'chat') { if (options.buttons.type === 'chat') {
content = options.buttons.body; content = options.buttons.body;
@@ -209,24 +544,17 @@ exports.LoadUtils = () => {
} else { } else {
caption = options.caption ? options.caption : ' '; //Caption can't be empty caption = options.caption ? options.caption : ' '; //Caption can't be empty
} }
buttonOptions = window.WWebJS.prepareMessageButtons(options.buttons);
buttonOptions = { buttonOptions = {
productHeaderImageRejected: false, ...buttonOptions,
isFromTemplate: false,
isDynamicReplyButtonsMsg: true,
title: options.buttons.title ? options.buttons.title : undefined,
footer: options.buttons.footer ? options.buttons.footer : undefined,
dynamicReplyButtons: options.buttons.buttons,
replyButtons: options.buttons.buttons,
caption: caption caption: caption
}; };
delete options.buttons; delete options.buttons;
} }
let listOptions = {}; let listOptions = {};
if(options.list){ if (options.list) {
if(window.Store.Conn.platform === 'smba' || window.Store.Conn.platform === 'smbi'){
throw '[LT01] Whatsapp business can\'t send this yet';
}
listOptions = { listOptions = {
type: 'list', type: 'list',
footer: options.list.footer, footer: options.list.footer,