Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions src/api/dto/group.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export class GroupSendInvite {
export class GroupUpdateParticipantDto extends GroupJid {
action: 'add' | 'remove' | 'promote' | 'demote';
participants: string[];
/** Sends a private invite when an add action is blocked by WhatsApp privacy settings. */
inviteOnAddFailure?: boolean;
/** Optional caption used for the private invite message. */
inviteCaption?: string;
}

export class GroupUpdateSettingDto extends GroupJid {
Expand Down
75 changes: 73 additions & 2 deletions src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import makeWASocket, {
downloadMediaMessage,
generateWAMessageFromContent,
getAggregateVotesInPollMessage,
getBinaryNodeChild,
GetCatalogOptions,
getContentType,
getDevice,
Expand Down Expand Up @@ -4568,17 +4569,87 @@ export class BaileysStartupService extends ChannelStartupService {
public async updateGParticipant(update: GroupUpdateParticipantDto) {
try {
const participants = update.participants.map((p) => createJid(p));
const updateParticipants = await this.client.groupParticipantsUpdate(
const updateParticipants = (await this.client.groupParticipantsUpdate(
update.groupJid,
participants,
update.action,
);
)) as any[];

if (update.action === 'add' && update.inviteOnAddFailure) {
const metadata = await this.client.groupMetadata(update.groupJid).catch(() => null);

for await (const participant of updateParticipants) {
if (participant.status !== '403') {
continue;
}

const addRequest = getBinaryNodeChild(participant.content, 'add_request');
const inviteCode = addRequest?.attrs?.code;
const inviteExpiration = addRequest?.attrs?.expiration;

if (!inviteCode || !inviteExpiration) {
participant.invite = {
sent: false,
reason: 'missing_add_request',
};
continue;
}

try {
await this.sendGroupInviteV4({
groupJid: update.groupJid,
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
recipientJid: participant.jid,
inviteCode,
inviteExpiration,
groupName: metadata?.subject ?? '',
caption: update.inviteCaption,
});

participant.invite = {
sent: true,
code: inviteCode,
expiration: inviteExpiration,
};
} catch (error) {
participant.invite = {
sent: false,
reason: error?.toString(),
};
}
}
}

return { updateParticipants: updateParticipants };
} catch (error) {
throw new BadRequestException('Error updating participants', error.toString());
}
}

private async sendGroupInviteV4(data: {
groupJid: string;
recipientJid: string;
inviteCode: string;
inviteExpiration: string | number;
groupName: string;
caption?: string;
}) {
const message = generateWAMessageFromContent(
data.recipientJid,
proto.Message.fromObject({
groupInviteMessage: {
groupJid: data.groupJid,
inviteCode: data.inviteCode,
inviteExpiration: Number(data.inviteExpiration),
groupName: data.groupName,
caption: data.caption ?? 'You have been invited to join this WhatsApp group.',
},
}),
{ userJid: data.recipientJid },
);

await this.client.relayMessage(data.recipientJid, message.message, { messageId: message.key.id });
}

public async updateGSetting(update: GroupUpdateSettingDto) {
try {
const updateSetting = await this.client.groupSettingUpdate(update.groupJid, update.action);
Expand Down
11 changes: 10 additions & 1 deletion src/validate/group.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,18 @@ export const updateParticipantsSchema: JSONSchema7 = {
description: '"participants" must be an array of numeric strings',
},
},
inviteOnAddFailure: {
type: 'boolean',
enum: [true, false],
description: 'When true and action is "add", send a private invite if WhatsApp blocks the add with an add_request response.',
},
inviteCaption: {
type: 'string',
description: 'Optional caption for the private invite message sent when inviteOnAddFailure is enabled.',
},
},
required: ['groupJid', 'action', 'participants'],
...isNotEmpty('groupJid', 'action'),
...isNotEmpty('groupJid', 'action', 'inviteCaption'),
};

export const updateSettingsSchema: JSONSchema7 = {
Expand Down