Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/api/groups.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ import {
CreateGroupRequest,
DescriptionRequest,
GroupField,
GroupMembershipRequest,
GroupMembershipRequestActionRequest,
GroupMembershipRequestActionResult,
GroupParticipant,
GroupsListFields,
GroupsPaginationParams,
JoinGroupRequest,
JoinGroupResponse,
ParticipantsRequest,
SettingsMemberAddMode,
SettingsMembershipApproval,
SettingsSecurityChangeInfo,
SubjectRequest,
} from '../structures/groups.dto';
Expand Down Expand Up @@ -343,6 +347,88 @@ export class GroupsController {
return session.getMemberAddMode(id);
}

@Put(':id/settings/security/membership-approval')
@SessionApiParam
@GroupIdApiParam
@CheckPolicies(CanSession(Action.Send, FromParam('session')))
@ApiOperation({
summary: 'Update settings - approve new members',
description:
'Enables or disables admin approval for users requesting to join the group.',
})
setMembershipApprovalMode(
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
@Body() request: SettingsMembershipApproval,
): Promise<boolean> {
return session.setMembershipApprovalMode(
id,
request.newMembersApprovalRequired,
);
}

@Get(':id/settings/security/membership-approval')
@SessionApiParam
@GroupIdApiParam
@CheckPolicies(CanSession(Action.Read, FromParam('session')))
@ApiOperation({
summary: 'Get settings - approve new members',
description:
'Returns whether admin approval is required for users requesting to join the group.',
})
getMembershipApprovalMode(
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<SettingsMembershipApproval> {
return session.getMembershipApprovalMode(id);
}

@Get(':id/membership-requests')
@SessionApiParam
@GroupIdApiParam
@CheckPolicies(CanSession(Action.Read, FromParam('session')))
@ApiOperation({
summary: 'Get pending group membership requests',
})
getGroupMembershipRequests(
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<GroupMembershipRequest[]> {
return session.getGroupMembershipRequests(id);
}

@Post(':id/membership-requests/approve')
@HttpCode(HttpStatus.OK)
@SessionApiParam
@GroupIdApiParam
@CheckPolicies(CanSession(Action.Send, FromParam('session')))
@ApiOperation({
summary: 'Approve pending group membership requests',
})
approveGroupMembershipRequests(
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
@Body() request: GroupMembershipRequestActionRequest,
): Promise<GroupMembershipRequestActionResult[]> {
return session.approveGroupMembershipRequests(id, request);
}

@Post(':id/membership-requests/reject')
@HttpCode(HttpStatus.OK)
@SessionApiParam
@GroupIdApiParam
@CheckPolicies(CanSession(Action.Send, FromParam('session')))
@ApiOperation({
summary: 'Reject pending group membership requests',
})
rejectGroupMembershipRequests(
@WorkingSessionParam session: WhatsappSession,
@Param('id') id: string,
@Body() request: GroupMembershipRequestActionRequest,
): Promise<GroupMembershipRequestActionResult[]> {
return session.rejectGroupMembershipRequests(id, request);
}

@Get(':id/invite-code')
@SessionApiParam
@GroupIdApiParam
Expand Down
37 changes: 37 additions & 0 deletions src/core/abc/session.abc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,14 @@ import { EventMessageRequest } from '../../structures/events.dto';
import {
CreateGroupRequest,
GroupField,
GroupMembershipRequest,
GroupMembershipRequestActionRequest,
GroupMembershipRequestActionResult,
GroupParticipant,
GroupsListFields,
ParticipantsRequest,
SettingsMemberAddMode,
SettingsMembershipApproval,
SettingsSecurityChangeInfo,
} from '../../structures/groups.dto';
import { WAHAChatPresences } from '../../structures/presence.dto';
Expand Down Expand Up @@ -1064,6 +1068,39 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}

public getMembershipApprovalMode(
id: string,
): Promise<SettingsMembershipApproval> {
throw new NotImplementedByEngineError();
}

public setMembershipApprovalMode(
id: string,
value: boolean,
): Promise<boolean> {
throw new NotImplementedByEngineError();
}

public getGroupMembershipRequests(
id: string,
): Promise<GroupMembershipRequest[]> {
throw new NotImplementedByEngineError();
}

public approveGroupMembershipRequests(
id: string,
request: GroupMembershipRequestActionRequest,
): Promise<GroupMembershipRequestActionResult[]> {
throw new NotImplementedByEngineError();
}

public rejectGroupMembershipRequests(
id: string,
request: GroupMembershipRequestActionRequest,
): Promise<GroupMembershipRequestActionResult[]> {
throw new NotImplementedByEngineError();
}

public deleteGroup(id) {
throw new NotImplementedByEngineError();
}
Expand Down
55 changes: 54 additions & 1 deletion src/core/engines/webjs/groups.webjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ import { WebjsClientCore } from '@waha/core/engines/webjs/WebjsClientCore';
import {
GroupId,
GroupInfo,
GroupMembershipRequest,
GroupMembershipRequestActionResult,
GroupParticipant,
GroupParticipantRole,
} from '@waha/structures/groups.dto';
import {
GroupParticipantType,
GroupV2JoinEvent,
GroupV2LeaveEvent,
GroupV2MembershipRequestEvent,
GroupV2ParticipantsEvent,
GroupV2UpdateEvent,
} from '@waha/structures/groups.events.dto';
import {
GroupChat,
GroupMembershipRequest as WEBJSGroupMembershipRequest,
GroupNotification,
GroupNotificationTypes,
GroupParticipant as WEBJSGroupParticipant,
MembershipRequestActionResult as WEBJSMembershipRequestActionResult,
} from 'whatsapp-web.js';
import { isPnUser } from '@waha/core/utils/jids';
import { GetSerialized } from '@waha/core/utils/serialized';
Expand All @@ -36,12 +41,60 @@ function ToGroupInfo(
invite: invite,
membersCanAddNewMember: groupMetadata.memberAddMode === 'all_member_add',
membersCanSendMessages: groupMetadata.announce,
newMembersApprovalRequired: groupMetadata.membershipApprovalMode,
newMembersApprovalRequired: ToGroupMembershipApprovalRequired(
groupMetadata.membershipApprovalMode,
),
participants: participants,
};
return info;
}

export function ToGroupMembershipApprovalRequired(value: unknown): boolean {
return value === true || value === 1;
}

export function ToGroupMembershipRequest(
request: WEBJSGroupMembershipRequest,
): GroupMembershipRequest {
const requesterId = GetSerialized(request.id);
if (!requesterId) {
throw new Error('Unable to serialize group membership requester ID');
}
return {
requesterId: requesterId,
addedById: GetSerialized(request.addedBy),
parentGroupId: GetSerialized(request.parentGroupId),
requestMethod: request.requestMethod ?? null,
timestamp: request.t,
};
}

export function ToGroupMembershipRequestActionResult(
result: WEBJSMembershipRequestActionResult,
): GroupMembershipRequestActionResult {
return {
requesterId: result.requesterId,
error: result.error,
message: result.message,
};
}

export function ToGroupV2MembershipRequestEvent(
notification: GroupNotification,
): GroupV2MembershipRequestEvent | null {
if (!notification.chatId || !notification.author) {
return null;
}
return {
group: {
id: notification.chatId,
},
requesterId: notification.author,
timestamp: notification.timestamp,
_data: notification,
};
}

export async function ToGroupV2JoinEvent(
client: WebjsClientCore,
me: string,
Expand Down
115 changes: 115 additions & 0 deletions src/core/engines/webjs/session.webjs.core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ import {
} from '@waha/core/engines/webjs/ack.webjs';
import {
getParticipants,
ToGroupMembershipApprovalRequired,
ToGroupMembershipRequest,
ToGroupMembershipRequestActionResult,
ToGroupV2JoinEvent,
ToGroupV2LeaveEvent,
ToGroupV2MembershipRequestEvent,
ToGroupV2ParticipantsEvent,
ToGroupV2UpdateEvent,
} from '@waha/core/engines/webjs/groups.webjs';
Expand Down Expand Up @@ -106,10 +110,14 @@ import {
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
import {
CreateGroupRequest,
GroupMembershipRequest,
GroupMembershipRequestActionRequest,
GroupMembershipRequestActionResult,
GroupParticipant,
GroupSortField,
ParticipantsRequest,
SettingsMemberAddMode,
SettingsMembershipApproval,
SettingsSecurityChangeInfo,
} from '@waha/structures/groups.dto';
import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
Expand Down Expand Up @@ -1626,6 +1634,105 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return groupChat.setAddMembersAdminsOnly(!value);
}

@Activity()
public async getMembershipApprovalMode(
id: string,
): Promise<SettingsMembershipApproval> {
const membershipApprovalMode = await this.whatsapp.pupPage.evaluate(
async (groupId) => {
await window
.require('WAWebGroupQueryJob')
.queryAndUpdateGroupMetadataById({ id: groupId });
const groupWid = window
.require('WAWebWidFactory')
.createWid(groupId);
const group = await window
.require('WAWebCollections')
.Chat.find(groupWid);
return group.groupMetadata.membershipApprovalMode;
},
id,
);
return {
newMembersApprovalRequired: ToGroupMembershipApprovalRequired(
membershipApprovalMode,
),
};
}

@Activity()
public async setMembershipApprovalMode(
id: string,
value: boolean,
): Promise<boolean> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
const success = await this.whatsapp.pupPage.evaluate(
async (groupId, enabled) => {
// @ts-ignore
const chat = await window.WWebJS.getChat(groupId, {
getAsModel: false,
});
try {
await window
.require('WAWebSetPropertyGroupAction')
.setGroupProperty(
chat,
'membership_approval_mode',
enabled ? 1 : 0,
);
return true;
} catch (error) {
if (error.name === 'ServerStatusCodeError') {
return false;
}
throw error;
}
},
id,
value,
);

if (success) {
(groupChat as any).groupMetadata.membershipApprovalMode = value;
}
return success;
}

@Activity()
public async getGroupMembershipRequests(
id: string,
): Promise<GroupMembershipRequest[]> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
const requests = await groupChat.getGroupMembershipRequests();
return requests.map(ToGroupMembershipRequest);
}

@Activity()
public async approveGroupMembershipRequests(
id: string,
request: GroupMembershipRequestActionRequest,
): Promise<GroupMembershipRequestActionResult[]> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
const results = await groupChat.approveGroupMembershipRequests({
requesterIds: request.requesterIds,
sleep: [250, 500],
});
return results.map(ToGroupMembershipRequestActionResult);
}

@Activity()
public async rejectGroupMembershipRequests(
id: string,
request: GroupMembershipRequestActionRequest,
): Promise<GroupMembershipRequestActionResult[]> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
const results = await groupChat.rejectGroupMembershipRequests({
requesterIds: request.requesterIds,
sleep: [250, 500],
});
return results.map(ToGroupMembershipRequestActionResult);
}

public async getGroups(pagination: PaginationParams) {
const chats = await this.whatsapp.getChats();
const groups = lodash.filter(chats, (chat) => chat.isGroup);
Expand Down Expand Up @@ -2308,6 +2415,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
.get(WAHAEvents.GROUP_V2_PARTICIPANTS)
.switch(groupV2Participants);

const groupMembershipRequest$ = fromEvent<GroupNotification>(
this.whatsapp,
Events.GROUP_MEMBERSHIP_REQUEST,
).pipe(map(ToGroupV2MembershipRequestEvent), filter(Boolean));
this.events2
.get(WAHAEvents.GROUP_V2_MEMBERSHIP_REQUEST)
.switch(groupMembershipRequest$);

const groupUpdate$ = fromEvent<GroupNotification>(
this.whatsapp,
Events.GROUP_UPDATE,
Expand Down
Loading