mirror of
https://github.com/cheveguerra/whatsapp-web.js.git
synced 2026-04-19 03:59:16 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14f7854bb8 | |||
| 22720038b5 | |||
| 83560a2b56 | |||
| bf93c6a10c | |||
| d7d7df4b74 | |||
| 7f62f2b62b | |||
| b4c2915334 | |||
|
|
e3442e5748 | ||
|
|
d03c1a65c2 | ||
|
|
9cb90ff457 | ||
|
|
9c6b1ab7b1 | ||
|
|
70b32705c9 | ||
|
|
ceabbd5177 | ||
|
|
ec0ee3c243 | ||
|
|
2ce2c7e97d | ||
|
|
eafdfe12f2 | ||
|
|
d126c35e1d | ||
|
|
8a84b5388d | ||
|
|
e810b5146d | ||
|
|
c0f9e78f6c | ||
|
|
7e3b2f6bde | ||
|
|
f655453ca4 | ||
|
|
2c084ef49e | ||
|
|
74a02cb736 | ||
|
|
9e55166164 | ||
| 29b61b396b | |||
|
|
8ae50ec99c | ||
|
|
2d6c73e010 | ||
| 025f9914c3 | |||
|
|
8a45a726ec | ||
|
|
d1b2df9051 | ||
|
|
c8fe80635a | ||
|
|
447c3c2a0f | ||
|
|
23d71e9f3b | ||
|
|
60fbd03256 | ||
|
|
d61a1c17d5 | ||
|
|
ceb83cbdec | ||
|
|
9bfea7847e | ||
|
|
045e7bd414 | ||
|
|
4c72c83dcd | ||
|
|
dfb862614c | ||
|
|
9184eca944 | ||
|
|
617ea37b71 | ||
|
|
aba0f3c3c9 | ||
|
|
48c8a498fa | ||
|
|
c32348d3cc | ||
|
|
4e324b140f | ||
|
|
4fe001f32f | ||
|
|
9ae8f44abc | ||
|
|
6de1ede32b | ||
|
|
19273a434e | ||
|
|
ef68f2f156 | ||
|
|
627b245143 | ||
|
|
aee6f057c1 | ||
|
|
383870184f | ||
|
|
6b98de9f4f | ||
|
|
81111faa05 | ||
|
|
b1693b49e0 |
32
example.js
32
example.js
@@ -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
10
index.d.ts
vendored
@@ -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 */
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,17 +68,46 @@ 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}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Buttons;
|
module.exports = Buttons;
|
||||||
|
|||||||
@@ -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 : ''
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ exports.ExposeStore = (moduleRaidStr) => {
|
|||||||
...window.mR.findModule('toWebpSticker')[0],
|
...window.mR.findModule('toWebpSticker')[0],
|
||||||
...window.mR.findModule('addWebpMetadata')[0]
|
...window.mR.findModule('addWebpMetadata')[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
window.Store.GroupUtils = {
|
window.Store.GroupUtils = {
|
||||||
...window.mR.findModule('createGroup')[0],
|
...window.mR.findModule('createGroup')[0],
|
||||||
...window.mR.findModule('setGroupDescription')[0],
|
...window.mR.findModule('setGroupDescription')[0],
|
||||||
@@ -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);
|
||||||
@@ -199,9 +534,9 @@ 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,
|
||||||
|
|||||||
Reference in New Issue
Block a user