fix+feat: addParticipants + createGroup (#2344)

* added required modules

* added sendGroupV4Invite implementation

* docs updated accordingly

* bug fixed

* added usage example

* ESLint fix

* extended resulting object

* docs updated accordingly

* added required modules

* updated comments

* bug fixed

* added usage example

* fixed to catch unexpected errors

* removed unnecessary module

* updated usage example

* minor fixes

* changed to return a string if an error is occupied

* fixed usage example

* removed unneccessary module

* added getAddParticipantsRpcResult

* updated addParticipants

- added AddParticipnatsOptions to provide values for sleep, autoSendInviteV4, comment
- combined addParticipants with sendGroupInviteMessage

* removed unneccessary sendGroupV4Invite

* docs updated

* fixed statement fot autoSendInviteV4

* minor fixes

- the returned value of addParticipants is no longer wrapped in an array
- docs updated
- fixed bug with the returned code value when the code is '200'

* added logic of retrieving the group profile picture

this allows to send an invitation with a profile picture thumbnail of the group

* minor fixes

* added fix for the inviteV4 to be sent

* updated usage example

* minor fix

* ESLint fix

* fix for code 419

* function name getProfilePictureThumb changed to be more clear

* refactor: modules moved to window.Store.GroupParticipants

* refactor: edited link of usage examples

* refactor: LidManipulations changed to LidUtils

* refactor: modules regrouping

To keep the modules in order were made some changes:
- JoinInviteV4 renamed to GroupInviteV4 and grouped with module sendGroupInviteMessage
- GroupParticipants grouped with module sendAddParticipantsRPC

* fix: injectToFunction fixed to work properly

* revert docs changes for Chat

* Merge branch 'main' into fix-add-participants

* docs fixed

* minor fix in injectToFunction

* removed data stringifying/parsing

* added random sleep time logic

* code refactoring

* docs updated

* addParticipants method refactored

* createGroup method extended

- added functionality to send private invitations automatically to missing participants
- added ability to provide value for message timer
- added ability to link a group to the community parent group at the stage of creating a group
- extended the returned data after the method call

* docs updated + typo fixed

* added ability to create group with myself only

* example updated

* docs clarified

* small code refactoring

* window.Store.Invite and window.Store.InviteInfo grouped

* window.compareWwebVersions fixed

* small update to improve the performance of findImpl fix

* gid as wid structure

* docs + example updated

* parenthesis

* avoid sleep after the last iteration

* fixed compatibility issue for sendGroupInviteMessage

* fixed issue with stale groupmetadata

in some cases after the participant has left the group, that participant is still stored in groupmetadata

* fixed compatibility issue for sendAddParticipantsRPC

* minor fix

* jsdocs fixed

* fixed issue with group creation

* fixed createGroup method

- added check for unregistered phone number
- extended returning data from method call: added title
- code refactored
- jsdocs fixed

* fixed addParticipants method

- added check for unregistered number
- code refactored

* docs fixed

* usage example fixed

* removed unnecessary method call

* jsdocs fixed

* check for group admin fixed
This commit is contained in:
alechkos 2023-10-11 16:10:26 +03:00 • committed by GitHub
commit c0705a2216
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 508 additions and 82 deletions

View file

@ -54,27 +54,140 @@ class GroupChat extends Chat {
}
/**
* Adds a list of participants by ID to the group
* @param {Array<string>} participantIds
* @returns {Promise<Object>}
* An object that handles the result for {@link addParticipants} method
* @typedef {Object} AddParticipantsResult
* @property {number} code The code of the result
* @property {string} message The result message
* @property {boolean} isInviteV4Sent Indicates if the inviteV4 was sent to the partitipant
*/
async addParticipants(participantIds) {
return await this.client.pupPage.evaluate(async (chatId, participantIds) => {
const chatWid = window.Store.WidFactory.createWid(chatId);
const chat = await window.Store.Chat.find(chatWid);
const participants = await Promise.all(participantIds.map(async p => {
const wid = window.Store.WidFactory.createWid(p);
return await window.Store.Contact.get(wid);
}));
await window.Store.GroupParticipants.addParticipants(chat, participants);
return { status: 200 };
}, this.id._serialized, participantIds);
/**
* An object that handles options for adding participants
* @typedef {Object} AddParticipnatsOptions
* @property {Array<number>|number} [sleep = [250, 500]] The number of milliseconds to wait before adding the next participant. If it is an array, a random sleep time between the sleep[0] and sleep[1] values will be added (the difference must be >=100 ms, otherwise, a random sleep time between sleep[1] and sleep[1] + 100 will be added). If sleep is a number, a sleep time equal to its value will be added. By default, sleep is an array with a value of [250, 500]
* @property {boolean} [autoSendInviteV4 = true] If true, the inviteV4 will be sent to those participants who have restricted others from being automatically added to groups, otherwise the inviteV4 won't be sent (true by default)
* @property {string} [comment = ''] The comment to be added to an inviteV4 (empty string by default)
*/
/**
* Adds a list of participants by ID to the group
* @param {string|Array<string>} participantIds
* @param {AddParticipnatsOptions} options An object thay handles options for adding participants
* @returns {Promise<Object.<string, AddParticipantsResult>|string>} Returns an object with the resulting data or an error message as a string
*/
async addParticipants(participantIds, options = {}) {
return await this.client.pupPage.evaluate(async (groupId, participantIds, options) => {
const { sleep = [250, 500], autoSendInviteV4 = true, comment = '' } = options;
const participantData = {};
!Array.isArray(participantIds) && (participantIds = [participantIds]);
const groupWid = window.Store.WidFactory.createWid(groupId);
const group = await window.Store.Chat.find(groupWid);
const participantWids = participantIds.map((p) => window.Store.WidFactory.createWid(p));
const errorCodes = {
default: 'An unknown error occupied while adding a participant',
isGroupEmpty: 'AddParticipantsError: The participant can\'t be added to an empty group',
iAmNotAdmin: 'AddParticipantsError: You have no admin rights to add a participant to a group',
200: 'The participant was added successfully',
403: 'The participant can be added by sending private invitation only',
404: 'The phone number is not registered on WhatsApp',
408: 'You cannot add this participant because they recently left the group',
409: 'The participant is already a group member',
417: 'The participant can\'t be added to the community. You can invite them privately to join this group through its invite link',
419: 'The participant can\'t be added because the group is full'
};
await window.Store.GroupMetadata.queryAndUpdate(groupWid);
const groupMetadata = group.groupMetadata;
const groupParticipants = groupMetadata?.participants;
if (!groupParticipants) {
return errorCodes.isGroupEmpty;
}
if (!group.iAmAdmin()) {
return errorCodes.iAmNotAdmin;
}
const _getSleepTime = (sleep) => {
if (!Array.isArray(sleep) || sleep.length === 2 && sleep[0] === sleep[1]) {
return sleep;
}
if (sleep.length === 1) {
return sleep[0];
}
(sleep[1] - sleep[0]) < 100 && (sleep[0] = sleep[1]) && (sleep[1] += 100);
return Math.floor(Math.random() * (sleep[1] - sleep[0] + 1)) + sleep[0];
};
for (const pWid of participantWids) {
const pId = pWid._serialized;
participantData[pId] = {
code: undefined,
message: undefined,
isInviteV4Sent: false
};
if (groupParticipants.some(p => p.id._serialized === pId)) {
participantData[pId].code = 409;
participantData[pId].message = errorCodes[409];
continue;
}
if (!(await window.Store.QueryExist(pWid))?.wid) {
participantData[pId].code = 404;
participantData[pId].message = errorCodes[404];
continue;
}
const rpcResult =
await window.WWebJS.getAddParticipantsRpcResult(groupMetadata, groupWid, pWid);
const { code: rpcResultCode } = rpcResult;
participantData[pId].code = rpcResultCode;
participantData[pId].message =
errorCodes[rpcResultCode] || errorCodes.default;
if (autoSendInviteV4 && rpcResultCode === 403) {
let userChat, isInviteV4Sent = false;
window.Store.ContactCollection.gadd(pWid, { silent: true });
if (rpcResult.name === 'ParticipantRequestCodeCanBeSent' &&
(userChat = await window.Store.Chat.find(pWid))) {
const groupName = group.formattedTitle || group.name;
const res = await window.Store.GroupInviteV4.sendGroupInviteMessage(
userChat,
group.id._serialized,
groupName,
rpcResult.inviteV4Code,
rpcResult.inviteV4CodeExp,
comment,
await window.WWebJS.getProfilePicThumbToBase64(groupWid)
);
isInviteV4Sent = window.compareWwebVersions(window.Debug.VERSION, '<', '2.2335.6')
? res === 'OK'
: res.messageSendResult === 'OK';
}
participantData[pId].isInviteV4Sent = isInviteV4Sent;
}
sleep &&
participantWids.length > 1 &&
participantWids.indexOf(pWid) !== participantWids.length - 1 &&
(await new Promise((resolve) => setTimeout(resolve, _getSleepTime(sleep))));
}
return participantData;
}, this.id._serialized, participantIds, options);
}
/**
* Removes a list of participants by ID to the group
* @param {Array<string>} participantIds
* @returns {Promise<Object>}
* @returns {Promise<{ status: number }>}
*/
async removeParticipants(participantIds) {
return await this.client.pupPage.evaluate(async (chatId, participantIds) => {
@ -246,7 +359,7 @@ class GroupChat extends Chat {
async getInviteCode() {
const codeRes = await this.client.pupPage.evaluate(async chatId => {
const chatWid = window.Store.WidFactory.createWid(chatId);
return window.Store.Invite.queryGroupInviteCode(chatWid);
return window.Store.GroupInvite.queryGroupInviteCode(chatWid);
}, this.id._serialized);
return codeRes.code;
@ -259,7 +372,7 @@ class GroupChat extends Chat {
async revokeInvite() {
const codeRes = await this.client.pupPage.evaluate(chatId => {
const chatWid = window.Store.WidFactory.createWid(chatId);
return window.Store.Invite.resetGroupInviteCode(chatWid);
return window.Store.GroupInvite.resetGroupInviteCode(chatWid);
}, this.id._serialized);
return codeRes.code;