Fix buttons list (#1649)

* DOCS: Buttons usage

* FIX: Limit buttons amount to avoid crashes

* STYLE: Fix for ESLINT test
This commit is contained in:
Sergio Carvalho
2022-08-14 16:12:01 -03:00
committed by GitHub
parent 627b245143
commit ef68f2f156
3 changed files with 276 additions and 56 deletions

View File

@@ -189,7 +189,20 @@ client.on('message', async msg => {
client.interface.openChatWindowAt(quotedMsg.id._serialized);
}
} 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',
[
{ body: 'whatsapp-web.js', url: 'https://wwebjs.dev/' },
{ body: 'Call me', number: '+1 (805) 457-4992' },
{ body: 'third special button', number: '+1 (202) 968-6161' },// Limited to 2 especial buttons, this one will be ignored
{ body: 'Some text' },
{ body: 'Another text' },
{ body: 'Another another text' },
{ body: 'Fourth button' }// Limited to 3 regular buttons, this one will be ignored
],
'title',
'footer'
);
client.sendMessage(msg.from, button);
} else if (msg.body === '!list') {
let sections = [{title:'sectionTitle',rows:[{title:'ListItem1', description: 'desc'},{title:'ListItem2'}]}];

View File

@@ -71,11 +71,18 @@ class Buttons {
* Returns: [{ buttonId:'customId',buttonText:{'displayText':'button1'},type: 1 },{buttonId:'n3XKsL',buttonText:{'displayText':'button2'},type:1},{buttonId:'NDJk0a',buttonText:{'displayText':'button3'},type:1}]
*/
_format(buttons){
buttons = buttons.slice(0,3); // phone users can only see 3 buttons, so lets limit this
// phone users can only see 3 regular buttons (not url or phone) and 2 especial buttons, so lets limit this
const especialButtons = buttons.filter(button => button.url || button.number).slice(0,2);
const regularButtons = buttons.filter(button => !button.url && !button.number).slice(0,3);
buttons = especialButtons.concat(regularButtons);
return buttons.map((btn) => {
if (btn.url && btn.number) throw 'button can\'t be with url and number together';
return {
buttonId: btn.id ? String(btn.id) : Util.generateHash(6),
buttonText: {displayText: btn.body},
url: btn.url,
phoneNumber: btn.number,
buttonText: btn.body,
type: 1
};
});
@@ -83,4 +90,4 @@ class Buttons {
}
module.exports = Buttons;
module.exports = Buttons;

View File

@@ -77,40 +77,112 @@ exports.ExposeStore = (moduleRaidStr) => {
window.mR.findModule(selector.name)[selector.index][selector.property] = (...args) => callback(oldFunct, args);
};
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());
return window.mR.findModule(
(m) =>
names.includes(
m.default?.prototype?.proxyName ||
m[name]?.prototype?.proxyName ||
m[baseName]?.prototype?.proxyName
)
);
};
window.injectToFunction({index: 0, name: 'createMsgProtobuf', property: 'createMsgProtobuf'}, (func, args) => {
window.injectToFunction({
index: 0,
name: 'createMsgProtobuf',
property: 'createMsgProtobuf'
}, (func, args) => {
const [message] = args;
const proto = func(...args);
if (proto.listMessage) {
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: {
listMessage: proto.listMessage
}
templateMessage: proto.templateMessage,
},
};
delete proto.listMessage;
delete proto.templateMessage;
}
if (proto.buttonsMessage) {
proto.viewOnceMessage = {
@@ -120,20 +192,91 @@ exports.ExposeStore = (moduleRaidStr) => {
};
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) => {
window.injectToFunction({
index: 0,
name: 'typeAttributeFromProtobuf',
property: 'typeAttributeFromProtobuf'
}, (func, args) => {
const [proto] = args;
if (
proto.buttonsMessage?.headerType === 1 ||
proto.buttonsMessage?.headerType === 2
) {
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';
}
return func(...args);
});
if (proto.listMessage) {
return 'text';
window.injectToFunction({
index: 0,
name: 'typeAttributeFromProtobuf',
property: 'typeAttributeFromProtobuf'
}, (func, args) => {
const [proto] = args;
if (proto.ephemeralMessage) {
const { message: n } = proto.ephemeralMessage;
return n ? func(n) : 'text';
}
if (proto.deviceSentMessage) {
const { message: n } = proto.deviceSentMessage;
return n ? func(n) : 'text';
}
if (proto.viewOnceMessage) {
const { message: n } = proto.viewOnceMessage;
return n ? func(n) : '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: n} = proto.deviceSentMessage;
return n ? func(n) : null;
}
if (proto.ephemeralMessage) {
const {message: n} = proto.ephemeralMessage;
return n ? func(n) : null;
}
if (proto.viewOnceMessage) {
const {message: n} = proto.viewOnceMessage;
return n ? func (n) : null;
}
return func(...args);
@@ -152,6 +295,75 @@ exports.LoadUtils = () => {
return false;
};
window.WWebJS.prepareMessageButtons = (buttonsOptions) => {
const returnObject = {};
(window.Store.ReplyButtonModel) || (window.Store.ReplyButtonModel = window.mR.findModule(m => m.default && m?.default?.prototype?.proxyName === 'replyButton')[0].default);
(window.Store.TemplateButtonModel) || (window.Store.TemplateButtonModel = window.mR.findModule(m => m.default && m?.default?.prototype?.proxyName === 'templateButton')[0].default);
(window.Store.TemplateButtonCollection) || (window.Store.TemplateButtonCollection = window.mR.findModule('TemplateButtonCollection')[0].TemplateButtonCollection);
(window.Store.ButtonCollection) || (window.Store.ButtonCollection = window.mR.findModule('ButtonCollection')[0].ButtonCollection);
if (!buttonsOptions.buttons) {
return returnObject;
}
if (!Array.isArray(buttonsOptions.buttons)) {
throw 'Buttons options is not a array';
}
returnObject.title = buttonsOptions.title;
returnObject.footer = buttonsOptions.footer;
returnObject.isFromTemplate = !0;
returnObject.buttons = new window.Store.TemplateButtonCollection;
returnObject.hydratedButtons = buttonsOptions.buttons.map((e, t) => {
if ('phoneNumber' in e) {
return {
index: t, callButton: {
displayText: e.buttonText, phoneNumber: e.phoneNumber
}
};
} else if ('url' in e) {
return {
index: t, urlButton: {
displayText: e.buttonText, url: e.url
}
};
} else {
return {
index: t, quickReplyButton: {
displayText: e.buttonText, id: e.buttonId || `${t}`
}
};
}
});
returnObject.buttons.add(returnObject.hydratedButtons.map((e, t) => {
var r, n, o, i;
const s = `${null != e.index ? e.index : t}`;
if (e.urlButton) {
return new window.Store.TemplateButtonModel({
id: s,
displayText: null === (r = e.urlButton) || void 0 === r ? void 0 : r.displayText,
url: null === (n = e.urlButton) || void 0 === n ? void 0 : n.url,
subtype: 'url'
});
} else if (e.callButton) {
return new window.Store.TemplateButtonModel({
id: s, displayText: e.callButton.displayText, phoneNumber: e.callButton.phoneNumber, subtype: 'call'
});
} else {
return new window.Store.TemplateButtonModel({
id: s,
displayText: null === (o = e.quickReplyButton) || void 0 === o ? void 0 : o.displayText,
selectionId: null === (i = e.quickReplyButton) || void 0 === i ? void 0 : i.id,
subtype: 'quick_reply'
});
}
}));
return returnObject;
};
window.WWebJS.sendMessage = async (chat, content, options = {}) => {
let attOptions = {};
@@ -240,7 +452,7 @@ exports.LoadUtils = () => {
}
}
}
let buttonOptions = {};
if(options.buttons){
let caption;
@@ -250,22 +462,10 @@ exports.LoadUtils = () => {
} else {
caption = options.caption ? options.caption : ' '; //Caption can't be empty
}
// UI needs to stop glitching
const ButtonsCollection = window.mR.findModule('ButtonCollection')[0].ButtonCollection;
const ReplyButtonModel = window.findProxyModel('ReplyButtonModel')[0].default;
const collection = new ButtonsCollection(ReplyButtonModel);
const quickButtons = options.buttons.buttons.map(a => {
return new ReplyButtonModel({id: a.buttonId, displayText: a.buttonText.displayText});
});
collection.add(quickButtons);
buttonOptions = window.WWebJS.prepareMessageButtons(options.buttons);
buttonOptions = {
isDynamicReplyButtonsMsg: true,
title: options.buttons.title ? options.buttons.title : undefined,
footer: options.buttons.footer ? options.buttons.footer : undefined,
dynamicReplyButtons: options.buttons.buttons,
replyButtons: collection,
...buttonOptions,
caption: caption
};
delete options.buttons;