feat+fix: send vote messages & vote update event (#3913)

* add send vote function

* Update index.d.ts

* fix and add required modules

* fix poll update in group chat

* adjustments to improvements

* add example usages

* fix eslint

---------

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>
This commit is contained in:
RendyCraft047 2025-11-10 02:37:51 +07:00 • committed by GitHub
commit 0c36613cc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 51 additions and 11 deletions

View file

@ -365,7 +365,7 @@ client.on('message', async msg => {
let list = new List('List body', 'btnText', sections, 'Title', 'footer'); let list = new List('List body', 'btnText', sections, 'Title', 'footer');
client.sendMessage(msg.from, list); client.sendMessage(msg.from, list);
} else if (msg.body === '!reaction') { } else if (msg.body === '!reaction') {
msg.react('👍'); await msg.react('👍');
} else if (msg.body === '!sendpoll') { } else if (msg.body === '!sendpoll') {
/** By default the poll is created as a single choice poll: */ /** By default the poll is created as a single choice poll: */
await msg.reply(new Poll('Winter or Summer?', ['Winter', 'Summer'])); await msg.reply(new Poll('Winter or Summer?', ['Winter', 'Summer']));
@ -382,11 +382,20 @@ client.on('message', async msg => {
] ]
}) })
); );
} else if (msg.body === '!vote') {
if (msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.type === 'poll_creation') {
await quotedMsg.vote(msg.body.replace('!vote', ''));
} else {
msg.reply('Can only be used on poll messages');
}
}
} else if (msg.body === '!edit') { } else if (msg.body === '!edit') {
if (msg.hasQuotedMsg) { if (msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage(); const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.fromMe) { if (quotedMsg.fromMe) {
quotedMsg.edit(msg.body.replace('!edit', '')); await quotedMsg.edit(msg.body.replace('!edit', ''));
} else { } else {
msg.reply('I can only edit my own messages'); msg.reply('I can only edit my own messages');
} }

4
index.d.ts vendored
View file

@ -1210,6 +1210,10 @@ declare namespace WAWebJS {
* Once the event is canceled, it can not be edited. * Once the event is canceled, it can not be edited.
*/ */
editScheduledEvent: (editedEventObject: Event) => Promise<Message | null>, editScheduledEvent: (editedEventObject: Event) => Promise<Message | null>,
/**
* Send votes to the poll message
*/
vote: (selectedOptions: Array<string>) => Promise<void>,
} }
/** ID that represents a message */ /** ID that represents a message */

View file

@ -6,7 +6,7 @@ const moduleRaid = require('@pedroslopez/moduleraid/moduleraid');
const Util = require('./util/Util'); const Util = require('./util/Util');
const InterfaceController = require('./util/InterfaceController'); const InterfaceController = require('./util/InterfaceController');
const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./util/Constants'); const { WhatsWebURL, DefaultOptions, Events, WAState, MessageTypes } = require('./util/Constants');
const { ExposeAuthStore } = require('./util/Injected/AuthStore/AuthStore'); const { ExposeAuthStore } = require('./util/Injected/AuthStore/AuthStore');
const { ExposeStore } = require('./util/Injected/Store'); const { ExposeStore } = require('./util/Injected/Store');
const { ExposeLegacyAuthStore } = require('./util/Injected/AuthStore/LegacyAuthStore'); const { ExposeLegacyAuthStore } = require('./util/Injected/AuthStore/LegacyAuthStore');
@ -2425,7 +2425,7 @@ class Client extends EventEmitter {
async getPollVotes(messageId) { async getPollVotes(messageId) {
const msg = await this.getMessageById(messageId); const msg = await this.getMessageById(messageId);
if (!msg) return []; if (!msg) return [];
if (msg.type != 'poll_creation') throw 'Invalid usage! Can only be used with a pollCreation message'; if (msg.type != MessageTypes.POLL_CREATION) throw 'Invalid usage! Can only be used with a pollCreation message';
const pollVotes = await this.pupPage.evaluate( async (msg) => { const pollVotes = await this.pupPage.evaluate( async (msg) => {
const msgKey = window.Store.MsgKey.fromString(msg.id._serialized); const msgKey = window.Store.MsgKey.fromString(msg.id._serialized);

View file

@ -749,6 +749,32 @@ class Message extends Base {
async getPollVotes() { async getPollVotes() {
return await this.client.getPollVotes(this.id._serialized); return await this.client.getPollVotes(this.id._serialized);
} }
/**
* Send votes to the poll message
* @param {Array<string>} selectedOptions Array of options selected.
* @returns {Promise}
*/
async vote(selectedOptions) {
if (this.type != MessageTypes.POLL_CREATION) throw 'Invalid usage! Can only be used with a pollCreation message';
await this.client.pupPage.evaluate(async (messageId, votes) => {
if (!messageId) return null;
if (!Array.isArray(votes)) votes = [votes];
let localIdSet = new Set();
const msg =
window.Store.Msg.get(messageId) || (await window.Store.Msg.getMessagesById([messageId]))?.messages?.[0];
if (!msg) return null;
msg.pollOptions.forEach(a => {
for (const option of votes) {
if (a.name === option) localIdSet.add(a.localId);
}
});
await window.Store.PollsSendVote.sendVote(msg, localIdSet);
}, this.id._serialized, selectedOptions);
}
} }
module.exports = Message; module.exports = Message;

View file

@ -103,7 +103,8 @@ exports.ExposeStore = () => {
window.Store.FindOrCreateChat = window.require('WAWebFindChatAction'); window.Store.FindOrCreateChat = window.require('WAWebFindChatAction');
window.Store.CustomerNoteUtils = window.require('WAWebNoteAction'); window.Store.CustomerNoteUtils = window.require('WAWebNoteAction');
window.Store.BusinessGatingUtils = window.require('WAWebBizGatingUtils'); window.Store.BusinessGatingUtils = window.require('WAWebBizGatingUtils');
window.Store.PollsVotesSchema = require('WAWebPollsVotesSchema'); window.Store.PollsVotesSchema = window.require('WAWebPollsVotesSchema');
window.Store.PollsSendVote = window.require('WAWebPollsSendVoteMsgAction');
window.Store.Settings = { window.Store.Settings = {
...window.require('WAWebUserPrefsGeneral'), ...window.require('WAWebUserPrefsGeneral'),

View file

@ -92,15 +92,15 @@ exports.LoadUtils = () => {
delete options.location; delete options.location;
} }
let _pollOptions = {}; let pollOptions = {};
if (options.poll) { if (options.poll) {
const { pollName, pollOptions } = options.poll; const { pollName, pollOptions: _pollOptions } = options.poll;
const { allowMultipleAnswers, messageSecret } = options.poll.options; const { allowMultipleAnswers, messageSecret } = options.poll.options;
_pollOptions = { pollOptions = {
kind: 'pollCreation', kind: 'pollCreation',
type: 'poll_creation', type: 'poll_creation',
pollName: pollName, pollName: pollName,
pollOptions: pollOptions, pollOptions: _pollOptions,
pollSelectableOptionsCount: allowMultipleAnswers ? 0 : 1, pollSelectableOptionsCount: allowMultipleAnswers ? 0 : 1,
messageSecret: messageSecret:
Array.isArray(messageSecret) && messageSecret.length === 32 Array.isArray(messageSecret) && messageSecret.length === 32
@ -264,7 +264,7 @@ exports.LoadUtils = () => {
id: newMsgKey, id: newMsgKey,
ack: 0, ack: 0,
body: content, body: content,
from: meUser, from: from,
to: chat.id, to: chat.id,
local: true, local: true,
self: 'out', self: 'out',
@ -276,7 +276,7 @@ exports.LoadUtils = () => {
...(mediaOptions.toJSON ? mediaOptions.toJSON() : {}), ...(mediaOptions.toJSON ? mediaOptions.toJSON() : {}),
...quotedMsgOptions, ...quotedMsgOptions,
...locationOptions, ...locationOptions,
..._pollOptions, ...pollOptions,
...eventOptions, ...eventOptions,
...vcardOptions, ...vcardOptions,
...buttonOptions, ...buttonOptions,