feat(groups): membership approval - settings, pending requests API and group.v2.participants.join-request event
Closes #2200 Co-authored-by: icecubex300 <83597812+icecubex300@users.noreply.github.com>
This commit is contained in:
1 parent
b5e223a333
commit
cc12380e48
21 files changed
+1553
-1
No files matched your search
@@ -32,6 +32,8 @@ import {
|
||||
CreateGroupRequest,
|
||||
DescriptionRequest,
|
||||
GroupField,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupsListFields,
|
||||
GroupsPaginationParams,
|
||||
@@ -39,6 +41,7 @@ import {
|
||||
JoinGroupResponse,
|
||||
ParticipantsRequest,
|
||||
SettingsMemberAddMode,
|
||||
SettingsMembershipApproval,
|
||||
SettingsSecurityChangeInfo,
|
||||
SubjectRequest,
|
||||
} from '../structures/groups.dto';
|
||||
@@ -343,6 +346,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/participants/join-requests')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@CheckPolicies(CanSession(Action.Read, FromParam('session')))
|
||||
@ApiOperation({
|
||||
summary: 'Get pending requests to join the group',
|
||||
})
|
||||
getGroupJoinRequests(
|
||||
@WorkingSessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<GroupJoinRequest[]> {
|
||||
return session.getGroupJoinRequests(id);
|
||||
}
|
||||
|
||||
@Post(':id/participants/join-requests/approve')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@CheckPolicies(CanSession(Action.Send, FromParam('session')))
|
||||
@ApiOperation({
|
||||
summary: 'Approve pending requests to join the group',
|
||||
})
|
||||
approveGroupJoinRequests(
|
||||
@WorkingSessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
return session.approveGroupJoinRequests(id, request);
|
||||
}
|
||||
|
||||
@Post(':id/participants/join-requests/reject')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@CheckPolicies(CanSession(Action.Send, FromParam('session')))
|
||||
@ApiOperation({
|
||||
summary: 'Reject pending requests to join the group',
|
||||
})
|
||||
rejectGroupJoinRequests(
|
||||
@WorkingSessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
return session.rejectGroupJoinRequests(id, request);
|
||||
}
|
||||
|
||||
@Get(':id/invite-code')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
GroupDescriptionInput,
|
||||
GroupIdInput,
|
||||
GroupJoinInput,
|
||||
GroupMembershipApprovalInput,
|
||||
GroupParticipantsInput,
|
||||
GroupPictureInput,
|
||||
GroupsListInput,
|
||||
@@ -363,6 +364,108 @@ export class GroupTools extends McpController {
|
||||
});
|
||||
}
|
||||
|
||||
@Tool('groups-get-membership-approval', {
|
||||
title: 'Get membership approval setting',
|
||||
description:
|
||||
'Get whether admin approval is required for users requesting to join the group',
|
||||
inputSchema: GroupIdInput,
|
||||
annotations: {
|
||||
readOnlyHint: true,
|
||||
destructiveHint: false,
|
||||
idempotentHint: true,
|
||||
},
|
||||
})
|
||||
async getMembershipApproval({ session, id }: z.infer<typeof GroupIdInput>) {
|
||||
return this.textRequest({
|
||||
method: 'GET',
|
||||
url: `/api/${session}/groups/${id}/settings/security/membership-approval`,
|
||||
});
|
||||
}
|
||||
|
||||
@Tool('groups-set-membership-approval', {
|
||||
title: 'Set membership approval setting',
|
||||
description:
|
||||
'Enable or disable admin approval for users requesting to join the group',
|
||||
inputSchema: GroupMembershipApprovalInput,
|
||||
annotations: {
|
||||
readOnlyHint: false,
|
||||
destructiveHint: false,
|
||||
idempotentHint: true,
|
||||
},
|
||||
})
|
||||
async setMembershipApproval({
|
||||
session,
|
||||
id,
|
||||
...body
|
||||
}: z.infer<typeof GroupMembershipApprovalInput>) {
|
||||
return this.textRequest({
|
||||
method: 'PUT',
|
||||
url: `/api/${session}/groups/${id}/settings/security/membership-approval`,
|
||||
data: body,
|
||||
});
|
||||
}
|
||||
|
||||
@Tool('groups-get-join-requests', {
|
||||
title: 'Get pending requests to join the group',
|
||||
description: 'Get pending requests to join the group',
|
||||
inputSchema: GroupIdInput,
|
||||
annotations: {
|
||||
readOnlyHint: true,
|
||||
destructiveHint: false,
|
||||
idempotentHint: true,
|
||||
},
|
||||
})
|
||||
async getJoinRequests({ session, id }: z.infer<typeof GroupIdInput>) {
|
||||
return this.textRequest({
|
||||
method: 'GET',
|
||||
url: `/api/${session}/groups/${id}/participants/join-requests`,
|
||||
});
|
||||
}
|
||||
|
||||
@Tool('groups-approve-join-requests', {
|
||||
title: 'Approve pending requests to join the group',
|
||||
description: 'Approve pending requests to join the group',
|
||||
inputSchema: GroupParticipantsInput,
|
||||
annotations: {
|
||||
readOnlyHint: false,
|
||||
destructiveHint: false,
|
||||
idempotentHint: false,
|
||||
},
|
||||
})
|
||||
async approveJoinRequests({
|
||||
session,
|
||||
id,
|
||||
...body
|
||||
}: z.infer<typeof GroupParticipantsInput>) {
|
||||
return this.textRequest({
|
||||
method: 'POST',
|
||||
url: `/api/${session}/groups/${id}/participants/join-requests/approve`,
|
||||
data: body,
|
||||
});
|
||||
}
|
||||
|
||||
@Tool('groups-reject-join-requests', {
|
||||
title: 'Reject pending requests to join the group',
|
||||
description: 'Reject pending requests to join the group',
|
||||
inputSchema: GroupParticipantsInput,
|
||||
annotations: {
|
||||
readOnlyHint: false,
|
||||
destructiveHint: false,
|
||||
idempotentHint: false,
|
||||
},
|
||||
})
|
||||
async rejectJoinRequests({
|
||||
session,
|
||||
id,
|
||||
...body
|
||||
}: z.infer<typeof GroupParticipantsInput>) {
|
||||
return this.textRequest({
|
||||
method: 'POST',
|
||||
url: `/api/${session}/groups/${id}/participants/join-requests/reject`,
|
||||
data: body,
|
||||
});
|
||||
}
|
||||
|
||||
@Tool('groups-get-invite-code', {
|
||||
title: 'Get group invite code',
|
||||
description: 'Get the invite code for a group',
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
GroupsPaginationParams,
|
||||
JoinGroupRequest,
|
||||
ParticipantsRequest,
|
||||
SettingsMembershipApproval,
|
||||
SettingsSecurityChangeInfo,
|
||||
SubjectRequest,
|
||||
} from '@waha/structures/groups.dto';
|
||||
@@ -64,3 +65,10 @@ export const GroupParticipantsInput = DtoToZod(ParticipantsRequest).extend({
|
||||
session: SessionField,
|
||||
id: GroupIdField,
|
||||
});
|
||||
|
||||
export const GroupMembershipApprovalInput = DtoToZod(
|
||||
SettingsMembershipApproval,
|
||||
).extend({
|
||||
session: SessionField,
|
||||
id: GroupIdField,
|
||||
});
|
||||
@@ -106,10 +106,13 @@ import { EventMessageRequest } from '../../structures/events.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupField,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupsListFields,
|
||||
ParticipantsRequest,
|
||||
SettingsMemberAddMode,
|
||||
SettingsMembershipApproval,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '../../structures/groups.dto';
|
||||
import { WAHAChatPresences } from '../../structures/presence.dto';
|
||||
@@ -1069,6 +1072,37 @@ 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 getGroupJoinRequests(id: string): Promise<GroupJoinRequest[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public approveGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public rejectGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
import {
|
||||
GOWSGroupJoinRequest,
|
||||
GOWSGroupParticipant,
|
||||
GOWSGroupParticipantRequest,
|
||||
GroupInfoEvent,
|
||||
GroupInfoFull,
|
||||
GroupJoinRequestEvent,
|
||||
JoinedGroupEvent,
|
||||
} from '@waha/core/engines/gows/types.group';
|
||||
import { isPnUser, toCusFormat, toJID } from '@waha/core/utils/jids';
|
||||
import {
|
||||
GroupId,
|
||||
GroupInfo,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupParticipantRole,
|
||||
NormalizeJoinRequestMethod,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import {
|
||||
GroupParticipantsJoinRequestAction,
|
||||
GroupParticipantType,
|
||||
GroupV2JoinEvent,
|
||||
GroupV2LeaveEvent,
|
||||
GroupV2ParticipantsJoinRequestEvent,
|
||||
GroupV2ParticipantsEvent,
|
||||
GroupV2UpdateEvent,
|
||||
} from '@waha/structures/groups.events.dto';
|
||||
@@ -171,6 +179,74 @@ function ToGroupInfoPartial(group: GroupInfoEvent): GroupInfo | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ToGroupJoinRequest(
|
||||
request: GOWSGroupParticipantRequest,
|
||||
): GroupJoinRequest {
|
||||
const requestedAt = Date.parse(request.RequestedAt);
|
||||
return {
|
||||
requesterId: toCusFormat(request.JID),
|
||||
addedById: null,
|
||||
parentGroupId: null,
|
||||
requestMethod: null,
|
||||
timestamp: requestedAt ? Math.floor(requestedAt / 1000) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function ToGroupJoinRequestResult(
|
||||
participant: GOWSGroupParticipant,
|
||||
): GroupJoinRequestResult {
|
||||
return {
|
||||
requesterId: toCusFormat(participant.JID),
|
||||
success: !participant.Error,
|
||||
error: participant.Error || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function ToGroupParticipantsJoinRequestAction(
|
||||
event: GroupJoinRequestEvent,
|
||||
request: GOWSGroupJoinRequest,
|
||||
): GroupParticipantsJoinRequestAction | null {
|
||||
if (event.Action === 'created') {
|
||||
return GroupParticipantsJoinRequestAction.CREATED;
|
||||
}
|
||||
if (event.Action !== 'revoked') {
|
||||
return null;
|
||||
}
|
||||
// The wire has one 'revoked' change for both cases - tell them apart by who sent it:
|
||||
// the requester cancelled their own request, anyone else (an admin) rejected it
|
||||
const self =
|
||||
event.Sender === request.JID ||
|
||||
(!!event.SenderPN && event.SenderPN === request.PhoneNumber);
|
||||
if (self) {
|
||||
return GroupParticipantsJoinRequestAction.REVOKED;
|
||||
}
|
||||
return GroupParticipantsJoinRequestAction.REJECTED;
|
||||
}
|
||||
|
||||
export function ToGroupV2ParticipantsJoinRequestEvents(
|
||||
event: GroupJoinRequestEvent,
|
||||
): GroupV2ParticipantsJoinRequestEvent[] {
|
||||
const events: GroupV2ParticipantsJoinRequestEvent[] = [];
|
||||
for (const request of event.Requests || []) {
|
||||
const action = ToGroupParticipantsJoinRequestAction(event, request);
|
||||
if (!action) {
|
||||
continue;
|
||||
}
|
||||
events.push({
|
||||
group: {
|
||||
id: event.JID,
|
||||
},
|
||||
action: action,
|
||||
requesterId: toCusFormat(request.JID),
|
||||
requesterPn: toCusFormat(request.PhoneNumber) || null,
|
||||
requestMethod: NormalizeJoinRequestMethod(request.RequestMethod),
|
||||
timestamp: Date.now(),
|
||||
_data: event,
|
||||
});
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
export function ToGroupParticipants(
|
||||
participants: GOWSGroupParticipant[],
|
||||
): GroupParticipant[] {
|
||||
|
||||
@@ -19,6 +19,10 @@ export namespace messages {
|
||||
PROMOTE = 2,
|
||||
DEMOTE = 3
|
||||
}
|
||||
export enum ParticipantRequestAction {
|
||||
APPROVE = 0,
|
||||
REJECT = 1
|
||||
}
|
||||
export enum MediaType {
|
||||
IMAGE = 0,
|
||||
AUDIO = 1,
|
||||
@@ -2978,6 +2982,145 @@ export namespace messages {
|
||||
return UpdateParticipantsRequest.deserialize(bytes);
|
||||
}
|
||||
}
|
||||
export class UpdateRequestParticipantsRequest extends pb_1.Message {
|
||||
#one_of_decls: number[][] = [];
|
||||
constructor(data?: any[] | {
|
||||
session?: Session;
|
||||
jid?: string;
|
||||
participants?: string[];
|
||||
action?: ParticipantRequestAction;
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [3], this.#one_of_decls);
|
||||
if (!Array.isArray(data) && typeof data == "object") {
|
||||
if ("session" in data && data.session != undefined) {
|
||||
this.session = data.session;
|
||||
}
|
||||
if ("jid" in data && data.jid != undefined) {
|
||||
this.jid = data.jid;
|
||||
}
|
||||
if ("participants" in data && data.participants != undefined) {
|
||||
this.participants = data.participants;
|
||||
}
|
||||
if ("action" in data && data.action != undefined) {
|
||||
this.action = data.action;
|
||||
}
|
||||
}
|
||||
}
|
||||
get session() {
|
||||
return pb_1.Message.getWrapperField(this, Session, 1) as Session;
|
||||
}
|
||||
set session(value: Session) {
|
||||
pb_1.Message.setWrapperField(this, 1, value);
|
||||
}
|
||||
get has_session() {
|
||||
return pb_1.Message.getField(this, 1) != null;
|
||||
}
|
||||
get jid() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 2, "") as string;
|
||||
}
|
||||
set jid(value: string) {
|
||||
pb_1.Message.setField(this, 2, value);
|
||||
}
|
||||
get participants() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 3, []) as string[];
|
||||
}
|
||||
set participants(value: string[]) {
|
||||
pb_1.Message.setField(this, 3, value);
|
||||
}
|
||||
get action() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 4, ParticipantRequestAction.APPROVE) as ParticipantRequestAction;
|
||||
}
|
||||
set action(value: ParticipantRequestAction) {
|
||||
pb_1.Message.setField(this, 4, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
session?: ReturnType<typeof Session.prototype.toObject>;
|
||||
jid?: string;
|
||||
participants?: string[];
|
||||
action?: ParticipantRequestAction;
|
||||
}): UpdateRequestParticipantsRequest {
|
||||
const message = new UpdateRequestParticipantsRequest({});
|
||||
if (data.session != null) {
|
||||
message.session = Session.fromObject(data.session);
|
||||
}
|
||||
if (data.jid != null) {
|
||||
message.jid = data.jid;
|
||||
}
|
||||
if (data.participants != null) {
|
||||
message.participants = data.participants;
|
||||
}
|
||||
if (data.action != null) {
|
||||
message.action = data.action;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
const data: {
|
||||
session?: ReturnType<typeof Session.prototype.toObject>;
|
||||
jid?: string;
|
||||
participants?: string[];
|
||||
action?: ParticipantRequestAction;
|
||||
} = {};
|
||||
if (this.session != null) {
|
||||
data.session = this.session.toObject();
|
||||
}
|
||||
if (this.jid != null) {
|
||||
data.jid = this.jid;
|
||||
}
|
||||
if (this.participants != null) {
|
||||
data.participants = this.participants;
|
||||
}
|
||||
if (this.action != null) {
|
||||
data.action = this.action;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
serialize(w: pb_1.BinaryWriter): void;
|
||||
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
|
||||
const writer = w || new pb_1.BinaryWriter();
|
||||
if (this.has_session)
|
||||
writer.writeMessage(1, this.session, () => this.session.serialize(writer));
|
||||
if (this.jid.length)
|
||||
writer.writeString(2, this.jid);
|
||||
if (this.participants.length)
|
||||
writer.writeRepeatedString(3, this.participants);
|
||||
if (this.action != ParticipantRequestAction.APPROVE)
|
||||
writer.writeEnum(4, this.action);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): UpdateRequestParticipantsRequest {
|
||||
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new UpdateRequestParticipantsRequest();
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup())
|
||||
break;
|
||||
switch (reader.getFieldNumber()) {
|
||||
case 1:
|
||||
reader.readMessage(message.session, () => message.session = Session.deserialize(reader));
|
||||
break;
|
||||
case 2:
|
||||
message.jid = reader.readString();
|
||||
break;
|
||||
case 3:
|
||||
pb_1.Message.addToRepeatedField(message, 3, reader.readString());
|
||||
break;
|
||||
case 4:
|
||||
message.action = reader.readEnum();
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
serializeBinary(): Uint8Array {
|
||||
return this.serialize();
|
||||
}
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateRequestParticipantsRequest {
|
||||
return UpdateRequestParticipantsRequest.deserialize(bytes);
|
||||
}
|
||||
}
|
||||
export class GroupCodeRequest extends pb_1.Message {
|
||||
#one_of_decls: number[][] = [];
|
||||
constructor(data?: any[] | {
|
||||
@@ -11475,6 +11618,33 @@ export namespace messages {
|
||||
responseSerialize: (message: JsonList) => Buffer.from(message.serialize()),
|
||||
responseDeserialize: (bytes: Buffer) => JsonList.deserialize(new Uint8Array(bytes))
|
||||
},
|
||||
SetGroupJoinApprovalMode: {
|
||||
path: "/messages.MessageService/SetGroupJoinApprovalMode",
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestSerialize: (message: JidBoolRequest) => Buffer.from(message.serialize()),
|
||||
requestDeserialize: (bytes: Buffer) => JidBoolRequest.deserialize(new Uint8Array(bytes)),
|
||||
responseSerialize: (message: Empty) => Buffer.from(message.serialize()),
|
||||
responseDeserialize: (bytes: Buffer) => Empty.deserialize(new Uint8Array(bytes))
|
||||
},
|
||||
GetGroupRequestParticipants: {
|
||||
path: "/messages.MessageService/GetGroupRequestParticipants",
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestSerialize: (message: JidRequest) => Buffer.from(message.serialize()),
|
||||
requestDeserialize: (bytes: Buffer) => JidRequest.deserialize(new Uint8Array(bytes)),
|
||||
responseSerialize: (message: JsonList) => Buffer.from(message.serialize()),
|
||||
responseDeserialize: (bytes: Buffer) => JsonList.deserialize(new Uint8Array(bytes))
|
||||
},
|
||||
UpdateGroupRequestParticipants: {
|
||||
path: "/messages.MessageService/UpdateGroupRequestParticipants",
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestSerialize: (message: UpdateRequestParticipantsRequest) => Buffer.from(message.serialize()),
|
||||
requestDeserialize: (bytes: Buffer) => UpdateRequestParticipantsRequest.deserialize(new Uint8Array(bytes)),
|
||||
responseSerialize: (message: JsonList) => Buffer.from(message.serialize()),
|
||||
responseDeserialize: (bytes: Buffer) => JsonList.deserialize(new Uint8Array(bytes))
|
||||
},
|
||||
GetProfilePicture: {
|
||||
path: "/messages.MessageService/GetProfilePicture",
|
||||
requestStream: false,
|
||||
@@ -11858,6 +12028,9 @@ export namespace messages {
|
||||
abstract SetGroupAnnounce(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
|
||||
abstract SetGroupMemberAddMode(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
|
||||
abstract UpdateGroupParticipants(call: grpc_1.ServerUnaryCall<UpdateParticipantsRequest, JsonList>, callback: grpc_1.sendUnaryData<JsonList>): void;
|
||||
abstract SetGroupJoinApprovalMode(call: grpc_1.ServerUnaryCall<JidBoolRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
|
||||
abstract GetGroupRequestParticipants(call: grpc_1.ServerUnaryCall<JidRequest, JsonList>, callback: grpc_1.sendUnaryData<JsonList>): void;
|
||||
abstract UpdateGroupRequestParticipants(call: grpc_1.ServerUnaryCall<UpdateRequestParticipantsRequest, JsonList>, callback: grpc_1.sendUnaryData<JsonList>): void;
|
||||
abstract GetProfilePicture(call: grpc_1.ServerUnaryCall<ProfilePictureRequest, ProfilePictureResponse>, callback: grpc_1.sendUnaryData<ProfilePictureResponse>): void;
|
||||
abstract SendPresence(call: grpc_1.ServerUnaryCall<PresenceRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
|
||||
abstract SendChatPresence(call: grpc_1.ServerUnaryCall<ChatPresenceRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
|
||||
@@ -11992,6 +12165,15 @@ export namespace messages {
|
||||
UpdateGroupParticipants: GrpcUnaryServiceInterface<UpdateParticipantsRequest, JsonList> = (message: UpdateParticipantsRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, options?: grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, callback?: grpc_1.requestCallback<JsonList>): grpc_1.ClientUnaryCall => {
|
||||
return super.UpdateGroupParticipants(message, metadata, options, callback);
|
||||
};
|
||||
SetGroupJoinApprovalMode: GrpcUnaryServiceInterface<JidBoolRequest, Empty> = (message: JidBoolRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<Empty>, options?: grpc_1.CallOptions | grpc_1.requestCallback<Empty>, callback?: grpc_1.requestCallback<Empty>): grpc_1.ClientUnaryCall => {
|
||||
return super.SetGroupJoinApprovalMode(message, metadata, options, callback);
|
||||
};
|
||||
GetGroupRequestParticipants: GrpcUnaryServiceInterface<JidRequest, JsonList> = (message: JidRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, options?: grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, callback?: grpc_1.requestCallback<JsonList>): grpc_1.ClientUnaryCall => {
|
||||
return super.GetGroupRequestParticipants(message, metadata, options, callback);
|
||||
};
|
||||
UpdateGroupRequestParticipants: GrpcUnaryServiceInterface<UpdateRequestParticipantsRequest, JsonList> = (message: UpdateRequestParticipantsRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, options?: grpc_1.CallOptions | grpc_1.requestCallback<JsonList>, callback?: grpc_1.requestCallback<JsonList>): grpc_1.ClientUnaryCall => {
|
||||
return super.UpdateGroupRequestParticipants(message, metadata, options, callback);
|
||||
};
|
||||
GetProfilePicture: GrpcUnaryServiceInterface<ProfilePictureRequest, ProfilePictureResponse> = (message: ProfilePictureRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<ProfilePictureResponse>, options?: grpc_1.CallOptions | grpc_1.requestCallback<ProfilePictureResponse>, callback?: grpc_1.requestCallback<ProfilePictureResponse>): grpc_1.ClientUnaryCall => {
|
||||
return super.GetProfilePicture(message, metadata, options, callback);
|
||||
};
|
||||
|
||||
@@ -697,6 +697,17 @@ function deserialize_messages_UpdateParticipantsRequest(buffer_arg) {
|
||||
return gows_pb.UpdateParticipantsRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_messages_UpdateRequestParticipantsRequest(arg) {
|
||||
if (!(arg instanceof gows_pb.UpdateRequestParticipantsRequest)) {
|
||||
throw new Error('Expected argument of type messages.UpdateRequestParticipantsRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_messages_UpdateRequestParticipantsRequest(buffer_arg) {
|
||||
return gows_pb.UpdateRequestParticipantsRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_messages_UpsertLabelRequest(arg) {
|
||||
if (!(arg instanceof gows_pb.UpsertLabelRequest)) {
|
||||
throw new Error('Expected argument of type messages.UpsertLabelRequest');
|
||||
@@ -1073,6 +1084,39 @@ updateGroupParticipants: {
|
||||
responseSerialize: serialize_messages_JsonList,
|
||||
responseDeserialize: deserialize_messages_JsonList,
|
||||
},
|
||||
setGroupJoinApprovalMode: {
|
||||
path: '/messages.MessageService/SetGroupJoinApprovalMode',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: gows_pb.JidBoolRequest,
|
||||
responseType: gows_pb.Empty,
|
||||
requestSerialize: serialize_messages_JidBoolRequest,
|
||||
requestDeserialize: deserialize_messages_JidBoolRequest,
|
||||
responseSerialize: serialize_messages_Empty,
|
||||
responseDeserialize: deserialize_messages_Empty,
|
||||
},
|
||||
getGroupRequestParticipants: {
|
||||
path: '/messages.MessageService/GetGroupRequestParticipants',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: gows_pb.JidRequest,
|
||||
responseType: gows_pb.JsonList,
|
||||
requestSerialize: serialize_messages_JidRequest,
|
||||
requestDeserialize: deserialize_messages_JidRequest,
|
||||
responseSerialize: serialize_messages_JsonList,
|
||||
responseDeserialize: deserialize_messages_JsonList,
|
||||
},
|
||||
updateGroupRequestParticipants: {
|
||||
path: '/messages.MessageService/UpdateGroupRequestParticipants',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: gows_pb.UpdateRequestParticipantsRequest,
|
||||
responseType: gows_pb.JsonList,
|
||||
requestSerialize: serialize_messages_UpdateRequestParticipantsRequest,
|
||||
requestDeserialize: deserialize_messages_UpdateRequestParticipantsRequest,
|
||||
responseSerialize: serialize_messages_JsonList,
|
||||
responseDeserialize: deserialize_messages_JsonList,
|
||||
},
|
||||
//
|
||||
// Actions
|
||||
//
|
||||
|
||||
@@ -84,6 +84,7 @@ goog.exportSymbol('proto.messages.Pagination', null, global);
|
||||
goog.exportSymbol('proto.messages.PairCodeRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.PairCodeResponse', null, global);
|
||||
goog.exportSymbol('proto.messages.ParticipantAction', null, global);
|
||||
goog.exportSymbol('proto.messages.ParticipantRequestAction', null, global);
|
||||
goog.exportSymbol('proto.messages.PasskeyResponseRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.PhoneInfo', null, global);
|
||||
goog.exportSymbol('proto.messages.PollMessage', null, global);
|
||||
@@ -120,6 +121,7 @@ goog.exportSymbol('proto.messages.StreamEventsRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.SubscribePresenceRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.UpdateContactRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.UpdateParticipantsRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.UpdateRequestParticipantsRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.UpsertLabelRequest', null, global);
|
||||
goog.exportSymbol('proto.messages.VideoInfo', null, global);
|
||||
goog.exportSymbol('proto.messages.vCardContact', null, global);
|
||||
@@ -753,6 +755,27 @@ if (goog.DEBUG && !COMPILED) {
|
||||
*/
|
||||
proto.messages.UpdateParticipantsRequest.displayName = 'proto.messages.UpdateParticipantsRequest';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, proto.messages.UpdateRequestParticipantsRequest.repeatedFields_, null);
|
||||
};
|
||||
goog.inherits(proto.messages.UpdateRequestParticipantsRequest, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.displayName = 'proto.messages.UpdateRequestParticipantsRequest';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
@@ -7652,6 +7675,273 @@ proto.messages.UpdateParticipantsRequest.prototype.setAction = function(value) {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List of repeated fields within this message type.
|
||||
* @private {!Array<number>}
|
||||
* @const
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.repeatedFields_ = [3];
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.messages.UpdateRequestParticipantsRequest.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.messages.UpdateRequestParticipantsRequest} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
session: (f = msg.getSession()) && proto.messages.Session.toObject(includeInstance, f),
|
||||
jid: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
participantsList: (f = jspb.Message.getRepeatedField(msg, 3)) == null ? undefined : f,
|
||||
action: jspb.Message.getFieldWithDefault(msg, 4, 0)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.messages.UpdateRequestParticipantsRequest;
|
||||
return proto.messages.UpdateRequestParticipantsRequest.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.messages.UpdateRequestParticipantsRequest} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = new proto.messages.Session;
|
||||
reader.readMessage(value,proto.messages.Session.deserializeBinaryFromReader);
|
||||
msg.setSession(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setJid(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addParticipants(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {!proto.messages.ParticipantRequestAction} */ (reader.readEnum());
|
||||
msg.setAction(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.messages.UpdateRequestParticipantsRequest.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.messages.UpdateRequestParticipantsRequest} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getSession();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
1,
|
||||
f,
|
||||
proto.messages.Session.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getJid();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getParticipantsList();
|
||||
if (f.length > 0) {
|
||||
writer.writeRepeatedString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getAction();
|
||||
if (f !== 0.0) {
|
||||
writer.writeEnum(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional Session session = 1;
|
||||
* @return {?proto.messages.Session}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.getSession = function() {
|
||||
return /** @type{?proto.messages.Session} */ (
|
||||
jspb.Message.getWrapperField(this, proto.messages.Session, 1));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.messages.Session|undefined} value
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest} returns this
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.setSession = function(value) {
|
||||
return jspb.Message.setWrapperField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest} returns this
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.clearSession = function() {
|
||||
return this.setSession(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.hasSession = function() {
|
||||
return jspb.Message.getField(this, 1) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string jid = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.getJid = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest} returns this
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.setJid = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* repeated string participants = 3;
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.getParticipantsList = function() {
|
||||
return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 3));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!Array<string>} value
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest} returns this
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.setParticipantsList = function(value) {
|
||||
return jspb.Message.setField(this, 3, value || []);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @param {number=} opt_index
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest} returns this
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.addParticipants = function(value, opt_index) {
|
||||
return jspb.Message.addToRepeatedField(this, 3, value, opt_index);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the list making it empty but non-null.
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest} returns this
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.clearParticipantsList = function() {
|
||||
return this.setParticipantsList([]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional ParticipantRequestAction action = 4;
|
||||
* @return {!proto.messages.ParticipantRequestAction}
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.getAction = function() {
|
||||
return /** @type {!proto.messages.ParticipantRequestAction} */ (jspb.Message.getFieldWithDefault(this, 4, 0));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!proto.messages.ParticipantRequestAction} value
|
||||
* @return {!proto.messages.UpdateRequestParticipantsRequest} returns this
|
||||
*/
|
||||
proto.messages.UpdateRequestParticipantsRequest.prototype.setAction = function(value) {
|
||||
return jspb.Message.setProto3EnumField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
@@ -22845,6 +23135,14 @@ proto.messages.ParticipantAction = {
|
||||
DEMOTE: 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
proto.messages.ParticipantRequestAction = {
|
||||
APPROVE: 0,
|
||||
REJECT: 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
|
||||
@@ -15,12 +15,19 @@ import { Jid } from '@waha/core/engines/const';
|
||||
import { EventsFromObservable } from '@waha/core/engines/gows/EventsFromObservable';
|
||||
import { GowsEventStreamObservable } from '@waha/core/engines/gows/GowsEventStreamObservable';
|
||||
import {
|
||||
ToGroupJoinRequest,
|
||||
ToGroupJoinRequestResult,
|
||||
ToGroupParticipants,
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2ParticipantsJoinRequestEvents,
|
||||
ToGroupV2ParticipantsEvents,
|
||||
ToGroupV2UpdateEvent,
|
||||
} from '@waha/core/engines/gows/groups.gows';
|
||||
import {
|
||||
GOWSGroupParticipant,
|
||||
GOWSGroupParticipantRequest,
|
||||
} from '@waha/core/engines/gows/types.group';
|
||||
import { messages } from '@waha/core/engines/gows/grpc/gows';
|
||||
import {
|
||||
optional,
|
||||
@@ -116,11 +123,14 @@ import {
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupSortField,
|
||||
Participant,
|
||||
ParticipantsRequest,
|
||||
SettingsMemberAddMode,
|
||||
SettingsMembershipApproval,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import { ReplyToMessage } from '@waha/structures/message.dto';
|
||||
@@ -247,6 +257,7 @@ enum WhatsMeowEvent {
|
||||
// Groups
|
||||
GROUP_INFO = 'events.GroupInfo',
|
||||
JOINED_GROUP = 'events.JoinedGroup',
|
||||
GROUP_JOIN_REQUEST = 'gows.GroupJoinRequestEvent',
|
||||
// Labels
|
||||
LABEL_EDIT = 'events.LabelEdit',
|
||||
LABEL_ASSOCIATION_CHAT = 'events.LabelAssociationChat',
|
||||
@@ -753,6 +764,15 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
);
|
||||
this.events2.get(WAHAEvents.GROUP_V2_UPDATE).switch(groupV2Update$);
|
||||
|
||||
const groupV2ParticipantsJoinRequest$ = all$.pipe(
|
||||
onlyEvent(WhatsMeowEvent.GROUP_JOIN_REQUEST),
|
||||
map(ToGroupV2ParticipantsJoinRequestEvents),
|
||||
mergeMap((events) => events),
|
||||
);
|
||||
this.events2
|
||||
.get(WAHAEvents.GROUP_V2_PARTICIPANTS_JOIN_REQUEST)
|
||||
.switch(groupV2ParticipantsJoinRequest$);
|
||||
|
||||
// Label Events
|
||||
// First, create streams for the raw label events
|
||||
const labelEditEvents$ = all$.pipe(
|
||||
@@ -1683,6 +1703,79 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return;
|
||||
}
|
||||
|
||||
public async getMembershipApprovalMode(
|
||||
id: string,
|
||||
): Promise<SettingsMembershipApproval> {
|
||||
const group = await this.getGroup(id);
|
||||
return {
|
||||
newMembersApprovalRequired: !!group.IsJoinApprovalRequired,
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setMembershipApprovalMode(
|
||||
id: string,
|
||||
value: boolean,
|
||||
): Promise<boolean> {
|
||||
const req = new messages.JidBoolRequest({
|
||||
session: this.session,
|
||||
jid: id,
|
||||
value: value,
|
||||
});
|
||||
await promisify(this.client.SetGroupJoinApprovalMode)(req);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async getGroupJoinRequests(id: string): Promise<GroupJoinRequest[]> {
|
||||
const req = new messages.JidRequest({
|
||||
session: this.session,
|
||||
jid: id,
|
||||
});
|
||||
const response = await promisify(this.client.GetGroupRequestParticipants)(
|
||||
req,
|
||||
);
|
||||
const requests: GOWSGroupParticipantRequest[] = parseJsonList(response);
|
||||
return requests.map(ToGroupJoinRequest);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public approveGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const action = messages.ParticipantRequestAction.APPROVE;
|
||||
return this.updateRequestParticipants(id, request.participants, action);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public rejectGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const action = messages.ParticipantRequestAction.REJECT;
|
||||
return this.updateRequestParticipants(id, request.participants, action);
|
||||
}
|
||||
|
||||
private async updateRequestParticipants(
|
||||
id: string,
|
||||
participants: Array<Participant>,
|
||||
action: messages.ParticipantRequestAction,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const jids = participants.map((p) => normalizeJid(toJID(p.id)));
|
||||
const req = new messages.UpdateRequestParticipantsRequest({
|
||||
session: this.session,
|
||||
jid: id,
|
||||
participants: jids,
|
||||
action: action,
|
||||
});
|
||||
const response = await promisify(
|
||||
this.client.UpdateGroupRequestParticipants,
|
||||
)(req);
|
||||
const results: GOWSGroupParticipant[] = parseJsonList(response);
|
||||
return results.map(ToGroupJoinRequestResult);
|
||||
}
|
||||
|
||||
public deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface GOWSGroupParticipant {
|
||||
PhoneNumber: JID;
|
||||
IsAdmin: boolean;
|
||||
IsSuperAdmin: boolean;
|
||||
Error?: number; // set when the participant action failed
|
||||
}
|
||||
|
||||
export interface GroupInfoFull {
|
||||
@@ -107,3 +108,24 @@ export interface JoinedGroupEvent extends GroupInfoFull {
|
||||
Reason: string;
|
||||
Type: string;
|
||||
}
|
||||
|
||||
export interface GOWSGroupJoinRequest {
|
||||
JID: JID;
|
||||
PhoneNumber: JID; // empty string if not provided
|
||||
RequestMethod: string; // invite_link | linked_group_join | non_admin_add
|
||||
}
|
||||
|
||||
export interface GroupJoinRequestEvent {
|
||||
JID: JID;
|
||||
Sender?: JID;
|
||||
SenderPN?: JID;
|
||||
Timestamp: string;
|
||||
Action: string; // created | revoked
|
||||
Requests: GOWSGroupJoinRequest[];
|
||||
}
|
||||
|
||||
// GetGroupRequestParticipants RPC result item
|
||||
export interface GOWSGroupParticipantRequest {
|
||||
JID: JID;
|
||||
RequestedAt: string;
|
||||
}
|
||||
@@ -7,13 +7,18 @@ import type {
|
||||
import { getGroupInviteLink } from '@waha/core/abc/session.abc';
|
||||
import {
|
||||
GroupInfo,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupParticipantRole,
|
||||
NormalizeJoinRequestMethod,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import {
|
||||
GroupParticipantsJoinRequestAction,
|
||||
GroupParticipantType,
|
||||
GroupV2JoinEvent,
|
||||
GroupV2LeaveEvent,
|
||||
GroupV2ParticipantsJoinRequestEvent,
|
||||
GroupV2ParticipantsEvent,
|
||||
GroupV2UpdateEvent,
|
||||
} from '@waha/structures/groups.events.dto';
|
||||
@@ -164,6 +169,89 @@ export function ToGroupV2UpdateEvent(
|
||||
};
|
||||
}
|
||||
|
||||
// Raw <membership_approval_request> node attrs from groupRequestParticipantsList
|
||||
interface MembershipApprovalRequestAttrs {
|
||||
jid?: string;
|
||||
phone_number?: string;
|
||||
request_method?: string;
|
||||
request_time?: string;
|
||||
t?: string;
|
||||
}
|
||||
|
||||
export function ToGroupJoinRequest(
|
||||
attrs: MembershipApprovalRequestAttrs,
|
||||
): GroupJoinRequest {
|
||||
return {
|
||||
requesterId: toCusFormat(attrs.jid),
|
||||
requesterPn: toCusFormat(attrs.phone_number) || null,
|
||||
addedById: null,
|
||||
parentGroupId: null,
|
||||
requestMethod: NormalizeJoinRequestMethod(attrs.request_method),
|
||||
timestamp: Number(attrs.request_time || attrs.t) || 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function ToGroupJoinRequestResult(result: {
|
||||
status: string;
|
||||
jid: string | undefined;
|
||||
}): GroupJoinRequestResult {
|
||||
const success = result.status === '200';
|
||||
let error: number | undefined;
|
||||
if (!success) {
|
||||
error = Number(result.status) || undefined;
|
||||
}
|
||||
return {
|
||||
requesterId: toCusFormat(result.jid) || null,
|
||||
success: success,
|
||||
error: error,
|
||||
};
|
||||
}
|
||||
|
||||
interface GroupJoinRequestUpdate {
|
||||
id: string;
|
||||
author: string;
|
||||
authorPn?: string;
|
||||
participant: string;
|
||||
participantPn?: string;
|
||||
action: string;
|
||||
method?: string;
|
||||
}
|
||||
|
||||
function ToGroupParticipantsJoinRequestAction(
|
||||
action: string,
|
||||
): GroupParticipantsJoinRequestAction | null {
|
||||
switch (action) {
|
||||
case 'created':
|
||||
return GroupParticipantsJoinRequestAction.CREATED;
|
||||
case 'rejected':
|
||||
return GroupParticipantsJoinRequestAction.REJECTED;
|
||||
case 'revoked':
|
||||
return GroupParticipantsJoinRequestAction.REVOKED;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function ToGroupV2ParticipantsJoinRequestEvent(
|
||||
update: GroupJoinRequestUpdate,
|
||||
): GroupV2ParticipantsJoinRequestEvent | null {
|
||||
const action = ToGroupParticipantsJoinRequestAction(update.action);
|
||||
if (!action) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
group: {
|
||||
id: toCusFormat(update.id),
|
||||
},
|
||||
action: action,
|
||||
requesterId: toCusFormat(update.participant),
|
||||
requesterPn: toCusFormat(update.participantPn) || null,
|
||||
requestMethod: NormalizeJoinRequestMethod(update.method),
|
||||
timestamp: Date.now(),
|
||||
_data: update,
|
||||
};
|
||||
}
|
||||
|
||||
export function ToGroupV2LeaveEvent(
|
||||
me: Contact,
|
||||
update: GroupParticipantUpdate,
|
||||
|
||||
@@ -51,9 +51,12 @@ import {
|
||||
WhatsappSession,
|
||||
} from '@waha/core/abc/session.abc';
|
||||
import {
|
||||
ToGroupJoinRequest,
|
||||
ToGroupJoinRequestResult,
|
||||
ToGroupParticipant,
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2ParticipantsJoinRequestEvent,
|
||||
ToGroupV2Participants,
|
||||
ToGroupV2UpdateEvent,
|
||||
} from '@waha/core/engines/noweb/groups.noweb';
|
||||
@@ -154,9 +157,12 @@ import {
|
||||
import { BinaryFile, FileType, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
ParticipantsRequest,
|
||||
SettingsMemberAddMode,
|
||||
SettingsMembershipApproval,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import {
|
||||
@@ -2086,6 +2092,59 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return await this.sock.groupMemberAddMode(id, mode);
|
||||
}
|
||||
|
||||
public async getMembershipApprovalMode(
|
||||
id: string,
|
||||
): Promise<SettingsMembershipApproval> {
|
||||
const group = await this.getGroup(id);
|
||||
return { newMembersApprovalRequired: !!group.joinApprovalMode };
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setMembershipApprovalMode(
|
||||
id: string,
|
||||
value: boolean,
|
||||
): Promise<boolean> {
|
||||
const mode = value ? 'on' : 'off';
|
||||
await this.sock.groupJoinApprovalMode(id, mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async getGroupJoinRequests(id: string): Promise<GroupJoinRequest[]> {
|
||||
const requests = await this.sock.groupRequestParticipantsList(id);
|
||||
return requests.map(ToGroupJoinRequest);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async approveGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
return await this.updateGroupJoinRequests(id, request, 'approve');
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async rejectGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
return await this.updateGroupJoinRequests(id, request, 'reject');
|
||||
}
|
||||
|
||||
private async updateGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
action: 'approve' | 'reject',
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const participants = request.participants.map(getId);
|
||||
const results = await this.sock.groupRequestParticipantsUpdate(
|
||||
id,
|
||||
participants,
|
||||
action,
|
||||
);
|
||||
return results.map(ToGroupJoinRequestResult);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async leaveGroup(id) {
|
||||
return this.sock.groupLeave(id);
|
||||
@@ -2781,6 +2840,17 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
);
|
||||
this.events2.get(WAHAEvents.GROUP_V2_LEAVE).switch(groupV2Leave$);
|
||||
|
||||
const groupV2ParticipantsJoinRequest$: Observable<any> = fromEvent(
|
||||
this.sock.ev,
|
||||
'group.join-request',
|
||||
).pipe(
|
||||
map((event: any) => ToGroupV2ParticipantsJoinRequestEvent(event)),
|
||||
filter(Boolean),
|
||||
);
|
||||
this.events2
|
||||
.get(WAHAEvents.GROUP_V2_PARTICIPANTS_JOIN_REQUEST)
|
||||
.switch(groupV2ParticipantsJoinRequest$);
|
||||
|
||||
this.events2.get(WAHAEvents.PRESENCE_UPDATE).switch(
|
||||
fromEvent(this.sock.ev, 'presence.update').pipe(
|
||||
filter((presence: any) => this.jids.include(presence.id)),
|
||||
|
||||
@@ -2,21 +2,28 @@ import { WebjsClientCore } from '@waha/core/engines/webjs/WebjsClientCore';
|
||||
import {
|
||||
GroupId,
|
||||
GroupInfo,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupParticipantRole,
|
||||
NormalizeJoinRequestMethod,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import {
|
||||
GroupParticipantsJoinRequestAction,
|
||||
GroupParticipantType,
|
||||
GroupV2JoinEvent,
|
||||
GroupV2LeaveEvent,
|
||||
GroupV2ParticipantsJoinRequestEvent,
|
||||
GroupV2ParticipantsEvent,
|
||||
GroupV2UpdateEvent,
|
||||
} from '@waha/structures/groups.events.dto';
|
||||
import {
|
||||
GroupChat,
|
||||
GroupMembershipRequest as WEBJSGroupJoinRequest,
|
||||
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';
|
||||
@@ -36,12 +43,68 @@ 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 ToGroupJoinRequest(
|
||||
request: WEBJSGroupJoinRequest,
|
||||
): GroupJoinRequest {
|
||||
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: NormalizeJoinRequestMethod(request.requestMethod),
|
||||
timestamp: request.t,
|
||||
};
|
||||
}
|
||||
|
||||
export function ToGroupJoinRequestResults(
|
||||
result: WEBJSMembershipRequestActionResult,
|
||||
): GroupJoinRequestResult[] {
|
||||
// whatsapp-web.js may report one result for multiple requesters - flatten to one entry per requester
|
||||
let requesterIds: (string | null)[];
|
||||
if (Array.isArray(result.requesterId)) {
|
||||
requesterIds = result.requesterId;
|
||||
} else {
|
||||
requesterIds = [result.requesterId ?? null];
|
||||
}
|
||||
return requesterIds.map((requesterId) => ({
|
||||
requesterId: requesterId,
|
||||
success: !result.error,
|
||||
error: result.error,
|
||||
}));
|
||||
}
|
||||
|
||||
export function ToGroupV2ParticipantsJoinRequestEvent(
|
||||
notification: GroupNotification,
|
||||
): GroupV2ParticipantsJoinRequestEvent | null {
|
||||
if (!notification.chatId || !notification.author) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
group: {
|
||||
id: notification.chatId,
|
||||
},
|
||||
action: GroupParticipantsJoinRequestAction.CREATED,
|
||||
requesterId: notification.author,
|
||||
timestamp: notification.timestamp,
|
||||
_data: notification,
|
||||
};
|
||||
}
|
||||
|
||||
export async function ToGroupV2JoinEvent(
|
||||
client: WebjsClientCore,
|
||||
me: string,
|
||||
|
||||
@@ -15,8 +15,12 @@ import {
|
||||
} from '@waha/core/engines/webjs/ack.webjs';
|
||||
import {
|
||||
getParticipants,
|
||||
ToGroupMembershipApprovalRequired,
|
||||
ToGroupJoinRequest,
|
||||
ToGroupJoinRequestResults,
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2ParticipantsJoinRequestEvent,
|
||||
ToGroupV2ParticipantsEvent,
|
||||
ToGroupV2UpdateEvent,
|
||||
} from '@waha/core/engines/webjs/groups.webjs';
|
||||
@@ -106,10 +110,13 @@ import {
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupSortField,
|
||||
ParticipantsRequest,
|
||||
SettingsMemberAddMode,
|
||||
SettingsMembershipApproval,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
|
||||
@@ -1691,6 +1698,101 @@ 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 getGroupJoinRequests(id: string): Promise<GroupJoinRequest[]> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const requests = await groupChat.getGroupMembershipRequests();
|
||||
return requests.map(ToGroupJoinRequest);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async approveGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const results = await groupChat.approveGroupMembershipRequests({
|
||||
requesterIds: request.participants.map((participant) => participant.id),
|
||||
sleep: [250, 500],
|
||||
});
|
||||
return results.flatMap(ToGroupJoinRequestResults);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async rejectGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const results = await groupChat.rejectGroupMembershipRequests({
|
||||
requesterIds: request.participants.map((participant) => participant.id),
|
||||
sleep: [250, 500],
|
||||
});
|
||||
return results.flatMap(ToGroupJoinRequestResults);
|
||||
}
|
||||
|
||||
public async getGroups(pagination: PaginationParams) {
|
||||
const chats = await this.whatsapp.getChats();
|
||||
const groups = lodash.filter(chats, (chat) => chat.isGroup);
|
||||
@@ -2373,6 +2475,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
.get(WAHAEvents.GROUP_V2_PARTICIPANTS)
|
||||
.switch(groupV2Participants);
|
||||
|
||||
const groupV2ParticipantsJoinRequest$ = fromEvent<GroupNotification>(
|
||||
this.whatsapp,
|
||||
Events.GROUP_MEMBERSHIP_REQUEST,
|
||||
).pipe(map(ToGroupV2ParticipantsJoinRequestEvent), filter(Boolean));
|
||||
this.events2
|
||||
.get(WAHAEvents.GROUP_V2_PARTICIPANTS_JOIN_REQUEST)
|
||||
.switch(groupV2ParticipantsJoinRequest$);
|
||||
|
||||
const groupUpdate$ = fromEvent<GroupNotification>(
|
||||
this.whatsapp,
|
||||
Events.GROUP_UPDATE,
|
||||
|
||||
@@ -8,8 +8,10 @@ import {
|
||||
GroupParticipantRole,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import {
|
||||
GroupParticipantsJoinRequestAction,
|
||||
GroupParticipantType,
|
||||
GroupV2LeaveEvent,
|
||||
GroupV2ParticipantsJoinRequestEvent,
|
||||
GroupV2ParticipantsEvent,
|
||||
GroupV2UpdateEvent,
|
||||
} from '@waha/structures/groups.events.dto';
|
||||
@@ -203,6 +205,27 @@ export function WppGp2ToGroupV2Update(msg: any): GroupV2UpdateEvent {
|
||||
};
|
||||
}
|
||||
|
||||
export function WppGp2ToGroupV2ParticipantsJoinRequest(
|
||||
msg: any,
|
||||
): GroupV2ParticipantsJoinRequestEvent | null {
|
||||
// Same subtype whatsapp-web.js emits 'group_membership_request' for
|
||||
if (msg.subtype !== 'membership_approval_request') {
|
||||
return null;
|
||||
}
|
||||
if (!msg.chatId || !msg.author) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
group: {
|
||||
id: toCusFormat(msg.chatId),
|
||||
},
|
||||
action: GroupParticipantsJoinRequestAction.CREATED,
|
||||
requesterId: toCusFormat(msg.author),
|
||||
timestamp: msg.timestamp,
|
||||
_data: msg,
|
||||
};
|
||||
}
|
||||
|
||||
export function WppUpdateLabelToAssociations(
|
||||
data: WppUpdateLabelEvent,
|
||||
): LabelChatAssociation[] {
|
||||
|
||||
@@ -70,6 +70,7 @@ import { CallData } from '@waha/structures/calls.dto';
|
||||
import {
|
||||
GroupV2JoinEvent,
|
||||
GroupV2LeaveEvent,
|
||||
GroupV2ParticipantsJoinRequestEvent,
|
||||
GroupV2ParticipantsEvent,
|
||||
GroupV2UpdateEvent,
|
||||
} from '@waha/structures/groups.events.dto';
|
||||
@@ -110,6 +111,7 @@ import {
|
||||
} from '@wppconnect-team/wppconnect';
|
||||
import type { Logger as WinstonLogger } from 'winston';
|
||||
import {
|
||||
WppGp2ToGroupV2ParticipantsJoinRequest,
|
||||
WppGp2ToGroupV2Update,
|
||||
WppParticipantsIsMyJoin,
|
||||
WppParticipantsIsMyLeave,
|
||||
@@ -138,6 +140,7 @@ import { getSessionNamespace } from '@waha/config';
|
||||
import { killProcessesByPatterns } from '@waha/core/utils/processes';
|
||||
import { DistinctAck } from '@waha/core/utils/reactive';
|
||||
import { isJidGroup, toCusFormat } from '@waha/core/utils/jids';
|
||||
import { GetSerialized } from '@waha/core/utils/serialized';
|
||||
import { WAMedia } from '@waha/structures/media.dto';
|
||||
import * as lodash from 'lodash';
|
||||
import {
|
||||
@@ -161,11 +164,15 @@ import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupInfo,
|
||||
GroupJoinRequest,
|
||||
GroupJoinRequestResult,
|
||||
GroupParticipant,
|
||||
GroupParticipantRole,
|
||||
GroupSortField,
|
||||
NormalizeJoinRequestMethod,
|
||||
ParticipantsRequest,
|
||||
SettingsMemberAddMode,
|
||||
SettingsMembershipApproval,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import { ContactQuery, ContactRequest } from '@waha/structures/contacts.dto';
|
||||
@@ -1433,6 +1440,67 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
|
||||
);
|
||||
}
|
||||
|
||||
public async getMembershipApprovalMode(
|
||||
id: string,
|
||||
): Promise<SettingsMembershipApproval> {
|
||||
const group = await this.wpp!.getChatById(this.ensureSuffix(id));
|
||||
return {
|
||||
newMembersApprovalRequired:
|
||||
!!group?.groupMetadata?.membershipApprovalMode,
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public setMembershipApprovalMode(
|
||||
id: string,
|
||||
value: boolean,
|
||||
): Promise<boolean> {
|
||||
// Not in the GroupProperty enum, but wa-js passes the property through
|
||||
return this.wpp!.setGroupProperty(
|
||||
this.ensureSuffix(id),
|
||||
'membership_approval_mode' as GroupProperty,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async getGroupJoinRequests(id: string): Promise<GroupJoinRequest[]> {
|
||||
const requests = await this.wpp!.getGroupMembershipRequests(
|
||||
this.ensureSuffix(id),
|
||||
);
|
||||
return requests.map((request) => ({
|
||||
requesterId: GetSerialized(request.id),
|
||||
addedById: GetSerialized(request.addedBy),
|
||||
parentGroupId: GetSerialized(request.parentGroupId),
|
||||
requestMethod: NormalizeJoinRequestMethod(request.requestMethod),
|
||||
timestamp: request.t,
|
||||
}));
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async approveGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const results = await this.wpp!.approveGroupMembershipRequest(
|
||||
this.ensureSuffix(id),
|
||||
request.participants.map((participant) => participant.id),
|
||||
);
|
||||
return results.map(WppToGroupJoinRequestResult);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async rejectGroupJoinRequests(
|
||||
id: string,
|
||||
request: ParticipantsRequest,
|
||||
): Promise<GroupJoinRequestResult[]> {
|
||||
const results = await this.wpp!.rejectGroupMembershipRequest(
|
||||
this.ensureSuffix(id),
|
||||
request.participants.map((participant) => participant.id),
|
||||
);
|
||||
return results.map(WppToGroupJoinRequestResult);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public deleteGroup(id: string): Promise<boolean> {
|
||||
return this.wpp!.deleteChat(this.ensureSuffix(id));
|
||||
@@ -1927,6 +1995,20 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
|
||||
);
|
||||
this.events2.get(WAHAEvents.GROUP_V2_UPDATE).switch(groupV2Update$);
|
||||
|
||||
const groupV2ParticipantsJoinRequest$ = streams.onAnyMessage.pipe(
|
||||
map((p) => p.data),
|
||||
filter((msg) => msg?.type === MessageType.GP2),
|
||||
distinct((msg) => msg?.id, interval(60_000)),
|
||||
map(
|
||||
(msg): GroupV2ParticipantsJoinRequestEvent =>
|
||||
WppGp2ToGroupV2ParticipantsJoinRequest(msg),
|
||||
),
|
||||
filter(Boolean),
|
||||
);
|
||||
this.events2
|
||||
.get(WAHAEvents.GROUP_V2_PARTICIPANTS_JOIN_REQUEST)
|
||||
.switch(groupV2ParticipantsJoinRequest$);
|
||||
|
||||
//
|
||||
// Calls
|
||||
//
|
||||
@@ -2738,6 +2820,17 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
|
||||
}
|
||||
}
|
||||
|
||||
function WppToGroupJoinRequestResult(result: {
|
||||
error: any;
|
||||
wid: any;
|
||||
}): GroupJoinRequestResult {
|
||||
return {
|
||||
requesterId: GetSerialized(result.wid),
|
||||
success: !result.error,
|
||||
error: Number(result.error) || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function extractWppMessageId(response: unknown): string | null {
|
||||
if (!response || typeof response !== 'object') {
|
||||
return null;
|
||||
|
||||
@@ -17,6 +17,7 @@ export enum WAHAEvents {
|
||||
GROUP_V2_LEAVE = 'group.v2.leave',
|
||||
GROUP_V2_UPDATE = 'group.v2.update',
|
||||
GROUP_V2_PARTICIPANTS = 'group.v2.participants',
|
||||
GROUP_V2_PARTICIPANTS_JOIN_REQUEST = 'group.v2.participants.join-request',
|
||||
PRESENCE_UPDATE = 'presence.update',
|
||||
POLL_VOTE = 'poll.vote',
|
||||
POLL_VOTE_FAILED = 'poll.vote.failed',
|
||||
|
||||
@@ -30,6 +30,102 @@ export class SettingsMemberAddMode {
|
||||
membersCanAddNewMember: boolean = true;
|
||||
}
|
||||
|
||||
export class SettingsMembershipApproval {
|
||||
@IsBoolean()
|
||||
newMembersApprovalRequired: boolean = false;
|
||||
}
|
||||
|
||||
export enum GroupJoinRequestMethod {
|
||||
INVITE_LINK = 'invite_link',
|
||||
LINKED_GROUP_JOIN = 'linked_group_join',
|
||||
NON_ADMIN_ADD = 'non_admin_add',
|
||||
}
|
||||
|
||||
export function NormalizeJoinRequestMethod(
|
||||
value: string | null | undefined,
|
||||
): GroupJoinRequestMethod | null {
|
||||
// Engines report the method in different formats
|
||||
// NOWEB/GOWS use 'invite_link'
|
||||
// WEBJS/WPP use 'InviteLink'
|
||||
switch (value) {
|
||||
case 'InviteLink':
|
||||
case GroupJoinRequestMethod.INVITE_LINK:
|
||||
return GroupJoinRequestMethod.INVITE_LINK;
|
||||
case 'LinkedGroupJoin':
|
||||
case GroupJoinRequestMethod.LINKED_GROUP_JOIN:
|
||||
return GroupJoinRequestMethod.LINKED_GROUP_JOIN;
|
||||
case 'NonAdminAdd':
|
||||
case GroupJoinRequestMethod.NON_ADMIN_ADD:
|
||||
return GroupJoinRequestMethod.NON_ADMIN_ADD;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupJoinRequest {
|
||||
@ApiProperty({
|
||||
description: 'ID of the user requesting to join the group',
|
||||
example: '123456789@c.us',
|
||||
})
|
||||
requesterId: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Phone number ID of the requester, if known',
|
||||
example: '123456789@c.us',
|
||||
nullable: true,
|
||||
required: false,
|
||||
})
|
||||
requesterPn?: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'ID of the user who created the request',
|
||||
example: '123456789@c.us',
|
||||
nullable: true,
|
||||
})
|
||||
addedById: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'ID of the parent community group, if present',
|
||||
example: '123456789@g.us',
|
||||
nullable: true,
|
||||
})
|
||||
parentGroupId: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'How the request was created, for example non_admin_add, invite_link, or linked_group_join',
|
||||
example: 'invite_link',
|
||||
nullable: true,
|
||||
})
|
||||
requestMethod: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Unix timestamp when the request was created',
|
||||
example: 1666943582,
|
||||
})
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export class GroupJoinRequestResult {
|
||||
@ApiProperty({
|
||||
description: 'ID of the participant the action was applied to',
|
||||
example: '123456789@c.us',
|
||||
nullable: true,
|
||||
})
|
||||
requesterId: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
})
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
example: 404,
|
||||
})
|
||||
error?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
|
||||
@@ -69,3 +69,54 @@ export class GroupV2ParticipantsEvent {
|
||||
|
||||
_data: any;
|
||||
}
|
||||
|
||||
export enum GroupParticipantsJoinRequestAction {
|
||||
// A user requested to join the group
|
||||
CREATED = 'created',
|
||||
// An admin rejected the request
|
||||
REJECTED = 'rejected',
|
||||
// The requester cancelled their own request
|
||||
REVOKED = 'revoked',
|
||||
}
|
||||
|
||||
export class GroupV2ParticipantsJoinRequestEvent {
|
||||
group: GroupId;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'What happened to the request to join the group',
|
||||
enum: GroupParticipantsJoinRequestAction,
|
||||
example: GroupParticipantsJoinRequestAction.CREATED,
|
||||
})
|
||||
action: GroupParticipantsJoinRequestAction;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'ID of the user requesting to join the group',
|
||||
example: '123456789@lid',
|
||||
})
|
||||
requesterId: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Phone number ID of the requester, if known',
|
||||
example: '123456789@c.us',
|
||||
nullable: true,
|
||||
required: false,
|
||||
})
|
||||
requesterPn?: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'How the request was created, for example non_admin_add, invite_link, or linked_group_join',
|
||||
example: 'invite_link',
|
||||
nullable: true,
|
||||
required: false,
|
||||
})
|
||||
requestMethod?: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Unix timestamp',
|
||||
example: 1666943582,
|
||||
})
|
||||
timestamp: number;
|
||||
|
||||
_data: any;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { WAHAEvents } from '@waha/structures/enums.dto';
|
||||
import {
|
||||
GroupV2JoinEvent,
|
||||
GroupV2LeaveEvent,
|
||||
GroupV2ParticipantsJoinRequestEvent,
|
||||
GroupV2ParticipantsEvent,
|
||||
GroupV2UpdateEvent,
|
||||
} from '@waha/structures/groups.events.dto';
|
||||
@@ -43,3 +44,12 @@ export class WebhookGroupV2Participants extends WAHAWebhook {
|
||||
|
||||
payload: GroupV2ParticipantsEvent;
|
||||
}
|
||||
|
||||
export class WebhookGroupV2ParticipantsJoinRequest extends WAHAWebhook {
|
||||
@ApiProperty({
|
||||
description: 'When a user requests to join a group',
|
||||
})
|
||||
event = WAHAEvents.GROUP_V2_PARTICIPANTS_JOIN_REQUEST;
|
||||
|
||||
payload: GroupV2ParticipantsJoinRequestEvent;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
WebhookGroupV2Join,
|
||||
WebhookGroupV2Leave,
|
||||
WebhookGroupV2ParticipantsJoinRequest,
|
||||
WebhookGroupV2Participants,
|
||||
WebhookGroupV2Update,
|
||||
} from '@waha/structures/groups.webhooks.dto';
|
||||
@@ -45,6 +46,7 @@ const WAHA_WEBHOOKS = [
|
||||
WebhookGroupV2Leave,
|
||||
WebhookGroupV2Update,
|
||||
WebhookGroupV2Participants,
|
||||
WebhookGroupV2ParticipantsJoinRequest,
|
||||
WAHAWebhookPresenceUpdate,
|
||||
WAHAWebhookPollVote,
|
||||
WAHAWebhookPollVoteFailed,
|
||||
|
||||
Reference in new issue
Block a user