feat: vote_update event (#2596)
* Merge branch 'add-polls' into polls-ext * added listener to poll votes * added isUnvote property * added option for custom message secret * usage example updated * added logic for catching removed votes * usage example updated * fixed logic for poll vote events * temp usage example * No more `isCurrentState` * messageSecret converted to array Co-authored-by: tuyuribr <45042245+tuyuribr@users.noreply.github.com> * docs updated * we already have messageSecret in vote.parentMessage * PollVote.selectedOptions changed to be similar to pollOptions * docs fixed * window.injectToFunction clarifications * minor clarifications * fix: typo * style: fix broken link in readme file --------- Co-authored-by: tuyuribr <45042245+tuyuribr@users.noreply.github.com>
This commit is contained in:
parent
8c810a8462
commit
01714fe599
8 changed files with 132 additions and 3 deletions
|
|
@ -593,4 +593,9 @@ client.on('group_membership_request', async (notification) => {
|
||||||
/** You can approve or reject the newly appeared membership request: */
|
/** You can approve or reject the newly appeared membership request: */
|
||||||
await client.approveGroupMembershipRequestss(notification.chatId, notification.author);
|
await client.approveGroupMembershipRequestss(notification.chatId, notification.author);
|
||||||
await client.rejectGroupMembershipRequests(notification.chatId, notification.author);
|
await client.rejectGroupMembershipRequests(notification.chatId, notification.author);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
client.on('vote_update', (vote) => {
|
||||||
|
/** The vote that was affected: */
|
||||||
|
console.log(vote);
|
||||||
|
});
|
||||||
|
|
|
||||||
36
index.d.ts
vendored
36
index.d.ts
vendored
|
|
@ -371,6 +371,14 @@ declare namespace WAWebJS {
|
||||||
|
|
||||||
/** Emitted when the RemoteAuth session is saved successfully on the external Database */
|
/** Emitted when the RemoteAuth session is saved successfully on the external Database */
|
||||||
on(event: 'remote_session_saved', listener: () => void): this
|
on(event: 'remote_session_saved', listener: () => void): this
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted when some poll option is selected or deselected,
|
||||||
|
* shows a user's current selected option(s) on the poll
|
||||||
|
*/
|
||||||
|
on(event: 'vote_update', listener: (
|
||||||
|
vote: PollVote
|
||||||
|
) => void): this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Current connection information */
|
/** Current connection information */
|
||||||
|
|
@ -1018,6 +1026,34 @@ declare namespace WAWebJS {
|
||||||
constructor(pollName: string, pollOptions: Array<string>, options?: PollSendOptions)
|
constructor(pollName: string, pollOptions: Array<string>, options?: PollSendOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Represents a Poll Vote on WhatsApp */
|
||||||
|
export interface PollVote {
|
||||||
|
/** The person who voted */
|
||||||
|
voter: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selected poll option(s)
|
||||||
|
* If it's an empty array, the user hasn't selected any options on the poll,
|
||||||
|
* may occur when they deselected all poll options
|
||||||
|
*/
|
||||||
|
selectedOptions: SelectedPollOption[];
|
||||||
|
|
||||||
|
/** Timestamp the option was selected or deselected at */
|
||||||
|
interractedAtTs: number;
|
||||||
|
|
||||||
|
/** The poll creation message associated with the poll vote */
|
||||||
|
parentMessage: Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Selected poll option structure */
|
||||||
|
export interface SelectedPollOption {
|
||||||
|
/** The local selected option ID */
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
/** The option name */
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Label {
|
export interface Label {
|
||||||
/** Label name */
|
/** Label name */
|
||||||
name: string,
|
name: string,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const { ExposeStore, LoadUtils } = require('./util/Injected');
|
||||||
const ChatFactory = require('./factories/ChatFactory');
|
const ChatFactory = require('./factories/ChatFactory');
|
||||||
const ContactFactory = require('./factories/ContactFactory');
|
const ContactFactory = require('./factories/ContactFactory');
|
||||||
const WebCacheFactory = require('./webCache/WebCacheFactory');
|
const WebCacheFactory = require('./webCache/WebCacheFactory');
|
||||||
const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
|
const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, PollVote, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
|
||||||
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
||||||
const NoAuth = require('./authStrategies/NoAuth');
|
const NoAuth = require('./authStrategies/NoAuth');
|
||||||
|
|
||||||
|
|
@ -54,6 +54,7 @@ const NoAuth = require('./authStrategies/NoAuth');
|
||||||
* @fires Client#contact_changed
|
* @fires Client#contact_changed
|
||||||
* @fires Client#group_admin_changed
|
* @fires Client#group_admin_changed
|
||||||
* @fires Client#group_membership_request
|
* @fires Client#group_membership_request
|
||||||
|
* @fires Client#vote_update
|
||||||
*/
|
*/
|
||||||
class Client extends EventEmitter {
|
class Client extends EventEmitter {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
|
|
@ -685,6 +686,16 @@ class Client extends EventEmitter {
|
||||||
this.emit(Events.MESSAGE_CIPHERTEXT, new Message(this, msg));
|
this.emit(Events.MESSAGE_CIPHERTEXT, new Message(this, msg));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await page.exposeFunction('onPollVoteEvent', (vote) => {
|
||||||
|
const _vote = new PollVote(this, vote);
|
||||||
|
/**
|
||||||
|
* Emitted when some poll option is selected or deselected,
|
||||||
|
* shows a user's current selected option(s) on the poll
|
||||||
|
* @event Client#vote_update
|
||||||
|
*/
|
||||||
|
this.emit(Events.VOTE_UPDATE, _vote);
|
||||||
|
});
|
||||||
|
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
||||||
window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
|
window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
|
||||||
|
|
@ -709,6 +720,10 @@ class Client extends EventEmitter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window.Store.Chat.on('change:unreadCount', (chat) => {window.onChatUnreadCountEvent(chat);});
|
window.Store.Chat.on('change:unreadCount', (chat) => {window.onChatUnreadCountEvent(chat);});
|
||||||
|
window.Store.PollVote.on('add', (vote) => {
|
||||||
|
const pollVoteModel = window.WWebJS.getPollVoteModel(vote);
|
||||||
|
pollVoteModel && window.onPollVoteEvent(pollVoteModel);
|
||||||
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
const module = window.Store.createOrUpdateReactionsModule;
|
const module = window.Store.createOrUpdateReactionsModule;
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,7 @@ class Message extends Base {
|
||||||
this.allowMultipleAnswers = Boolean(!data.pollSelectableOptionsCount);
|
this.allowMultipleAnswers = Boolean(!data.pollSelectableOptionsCount);
|
||||||
this.pollInvalidated = data.pollInvalidated;
|
this.pollInvalidated = data.pollInvalidated;
|
||||||
this.isSentCagPollCreation = data.isSentCagPollCreation;
|
this.isSentCagPollCreation = data.isSentCagPollCreation;
|
||||||
|
this.messageSecret = Object.keys(data.messageSecret).map((key) => data.messageSecret[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super._patch(data);
|
return super._patch(data);
|
||||||
|
|
|
||||||
61
src/structures/PollVote.js
Normal file
61
src/structures/PollVote.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Message = require('./Message');
|
||||||
|
const Base = require('./Base');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selected poll option structure
|
||||||
|
* @typedef {Object} SelectedPollOption
|
||||||
|
* @property {number} id The local selected or deselected option ID
|
||||||
|
* @property {string} name The option name
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Poll Vote on WhatsApp
|
||||||
|
* @extends {Base}
|
||||||
|
*/
|
||||||
|
class PollVote extends Base {
|
||||||
|
constructor(client, data) {
|
||||||
|
super(client);
|
||||||
|
|
||||||
|
if (data) this._patch(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_patch(data) {
|
||||||
|
/**
|
||||||
|
* The person who voted
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.voter = data.sender;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selected poll option(s)
|
||||||
|
* If it's an empty array, the user hasn't selected any options on the poll,
|
||||||
|
* may occur when they deselected all poll options
|
||||||
|
* @type {SelectedPollOption[]}
|
||||||
|
*/
|
||||||
|
this.selectedOptions =
|
||||||
|
data.selectedOptionLocalIds.length > 0
|
||||||
|
? data.selectedOptionLocalIds.map((e) => ({
|
||||||
|
name: data.parentMessage.pollOptions.find((x) => x.localId === e).name,
|
||||||
|
localId: e
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp the option was selected or deselected at
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.interractedAtTs = data.senderTimestampMs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The poll creation message associated with the poll vote
|
||||||
|
* @type {Message}
|
||||||
|
*/
|
||||||
|
this.parentMessage = new Message(this.client, data.parentMessage);
|
||||||
|
|
||||||
|
return super._patch(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PollVote;
|
||||||
|
|
@ -20,4 +20,5 @@ module.exports = {
|
||||||
Payment: require('./Payment'),
|
Payment: require('./Payment'),
|
||||||
Reaction: require('./Reaction'),
|
Reaction: require('./Reaction'),
|
||||||
Poll: require('./Poll'),
|
Poll: require('./Poll'),
|
||||||
|
PollVote: require('./PollVote')
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,8 @@ exports.Events = {
|
||||||
STATE_CHANGED: 'change_state',
|
STATE_CHANGED: 'change_state',
|
||||||
BATTERY_CHANGED: 'change_battery',
|
BATTERY_CHANGED: 'change_battery',
|
||||||
INCOMING_CALL: 'call',
|
INCOMING_CALL: 'call',
|
||||||
REMOTE_SESSION_SAVED: 'remote_session_saved'
|
REMOTE_SESSION_SAVED: 'remote_session_saved',
|
||||||
|
VOTE_UPDATE: 'vote_update'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,15 @@ exports.LoadUtils = () => {
|
||||||
return msg;
|
return msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.WWebJS.getPollVoteModel = (vote) => {
|
||||||
|
const _vote = vote.serialize();
|
||||||
|
if (vote.parentMsgKey) {
|
||||||
|
const msg = window.Store.Msg.get(vote.parentMsgKey);
|
||||||
|
msg && (_vote.parentMessage = window.WWebJS.getMessageModel(msg));
|
||||||
|
return _vote;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
window.WWebJS.getChatModel = async chat => {
|
window.WWebJS.getChatModel = async chat => {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue