Skip to content

Commit

Permalink
feat: vote_update event (pedroslopez#2596)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
2 people authored and ninajika committed May 2, 2024
1 parent 637927f commit 9cac8fd
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 3 deletions.
7 changes: 6 additions & 1 deletion example.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,4 +593,9 @@ client.on('group_membership_request', async (notification) => {
/** You can approve or reject the newly appeared membership request: */
await client.approveGroupMembershipRequestss(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 changes: 36 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,14 @@ declare namespace WAWebJS {

/** Emitted when the RemoteAuth session is saved successfully on the external Database */
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 */
Expand Down Expand Up @@ -1021,6 +1029,34 @@ declare namespace WAWebJS {
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 {
/** Label name */
name: string,
Expand Down
17 changes: 16 additions & 1 deletion src/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const { ExposeStore, LoadUtils } = require('./util/Injected');
const ChatFactory = require('./factories/ChatFactory');
const ContactFactory = require('./factories/ContactFactory');
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 NoAuth = require('./authStrategies/NoAuth');

Expand Down Expand Up @@ -54,6 +54,7 @@ const NoAuth = require('./authStrategies/NoAuth');
* @fires Client#contact_changed
* @fires Client#group_admin_changed
* @fires Client#group_membership_request
* @fires Client#vote_update
*/
class Client extends EventEmitter {
constructor(options = {}) {
Expand Down Expand Up @@ -709,6 +710,16 @@ class Client extends EventEmitter {
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(() => {
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)); });
Expand All @@ -733,6 +744,10 @@ class Client extends EventEmitter {
}
});
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;
Expand Down
1 change: 1 addition & 0 deletions src/structures/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ class Message extends Base {
this.allowMultipleAnswers = Boolean(!data.pollSelectableOptionsCount);
this.pollInvalidated = data.pollInvalidated;
this.isSentCagPollCreation = data.isSentCagPollCreation;
this.messageSecret = Object.keys(data.messageSecret).map((key) => data.messageSecret[key]);
}

return super._patch(data);
Expand Down
61 changes: 61 additions & 0 deletions src/structures/PollVote.js
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions src/structures/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ module.exports = {
Payment: require('./Payment'),
Reaction: require('./Reaction'),
Poll: require('./Poll'),
PollVote: require('./PollVote')
};
3 changes: 2 additions & 1 deletion src/util/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ exports.Events = {
STATE_CHANGED: 'change_state',
BATTERY_CHANGED: 'change_battery',
INCOMING_CALL: 'call',
REMOTE_SESSION_SAVED: 'remote_session_saved'
REMOTE_SESSION_SAVED: 'remote_session_saved',
VOTE_UPDATE: 'vote_update'
};

/**
Expand Down
9 changes: 9 additions & 0 deletions src/util/Injected.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,15 @@ exports.LoadUtils = () => {
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 => {

Expand Down

0 comments on commit 9cac8fd

Please sign in to comment.