From 7c32d7f03e8df5c67272dfd8430f65321ebf1af1 Mon Sep 17 00:00:00 2001 From: hassankaid Date: Wed, 29 Apr 2026 17:04:22 +0100 Subject: [PATCH 1/2] feat(group): add updateMemberAddMode endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the WhatsApp protocol's group "member add mode" setting (supported by Baileys via groupMemberAddMode) as a new endpoint, so clients can toggle whether non-admin members are allowed to add participants to a group. POST /group/updateMemberAddMode/{instance}?groupJid= body: { "mode": "admin_add" | "all_member_add" } - admin_add → only admins can add new members - all_member_add → any member can add new members (WhatsApp default) Mirrors the existing pattern of updateSetting: - DTO : GroupUpdateMemberAddModeDto - Schema: updateMemberAddModeSchema (validates mode enum) - Service: WhatsappBaileysService.updateMemberAddMode - Controller: GroupController.updateMemberAddMode - Route: POST /group/updateMemberAddMode No breaking change. No new dependency. --- src/api/controllers/group.controller.ts | 5 +++++ src/api/dto/group.dto.ts | 4 ++++ .../channel/whatsapp/whatsapp.baileys.service.ts | 10 ++++++++++ src/api/routes/group.router.ts | 12 ++++++++++++ src/validate/group.schema.ts | 14 ++++++++++++++ 5 files changed, 45 insertions(+) diff --git a/src/api/controllers/group.controller.ts b/src/api/controllers/group.controller.ts index ebe7c0367..6c5904ccd 100644 --- a/src/api/controllers/group.controller.ts +++ b/src/api/controllers/group.controller.ts @@ -9,6 +9,7 @@ import { GroupSendInvite, GroupSubjectDto, GroupToggleEphemeralDto, + GroupUpdateMemberAddModeDto, GroupUpdateParticipantDto, GroupUpdateSettingDto, } from '@api/dto/group.dto'; @@ -74,6 +75,10 @@ export class GroupController { return await this.waMonitor.waInstances[instance.instanceName].updateGSetting(update); } + public async updateMemberAddMode(instance: InstanceDto, update: GroupUpdateMemberAddModeDto) { + return await this.waMonitor.waInstances[instance.instanceName].updateMemberAddMode(update); + } + public async toggleEphemeral(instance: InstanceDto, update: GroupToggleEphemeralDto) { return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(update); } diff --git a/src/api/dto/group.dto.ts b/src/api/dto/group.dto.ts index 293329d2e..06485bc37 100644 --- a/src/api/dto/group.dto.ts +++ b/src/api/dto/group.dto.ts @@ -54,3 +54,7 @@ export class GroupUpdateSettingDto extends GroupJid { export class GroupToggleEphemeralDto extends GroupJid { expiration: 0 | 86400 | 604800 | 7776000; } + +export class GroupUpdateMemberAddModeDto extends GroupJid { + mode: 'admin_add' | 'all_member_add'; +} diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 60e857fcc..82da81c23 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -26,6 +26,7 @@ import { GroupSendInvite, GroupSubjectDto, GroupToggleEphemeralDto, + GroupUpdateMemberAddModeDto, GroupUpdateParticipantDto, GroupUpdateSettingDto, } from '@api/dto/group.dto'; @@ -4588,6 +4589,15 @@ export class BaileysStartupService extends ChannelStartupService { } } + public async updateMemberAddMode(update: GroupUpdateMemberAddModeDto) { + try { + await this.client.groupMemberAddMode(update.groupJid, update.mode); + return { update: 'success', mode: update.mode }; + } catch (error) { + throw new BadRequestException('Error updating member add mode', error.toString()); + } + } + public async toggleEphemeral(update: GroupToggleEphemeralDto) { try { await this.client.groupToggleEphemeral(update.groupJid, update.expiration); diff --git a/src/api/routes/group.router.ts b/src/api/routes/group.router.ts index 7086b1176..8a87abdf9 100644 --- a/src/api/routes/group.router.ts +++ b/src/api/routes/group.router.ts @@ -10,6 +10,7 @@ import { GroupSendInvite, GroupSubjectDto, GroupToggleEphemeralDto, + GroupUpdateMemberAddModeDto, GroupUpdateParticipantDto, GroupUpdateSettingDto, } from '@api/dto/group.dto'; @@ -25,6 +26,7 @@ import { updateGroupDescriptionSchema, updateGroupPictureSchema, updateGroupSubjectSchema, + updateMemberAddModeSchema, updateParticipantsSchema, updateSettingsSchema, } from '@validate/validate.schema'; @@ -176,6 +178,16 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) + .post(this.routerPath('updateMemberAddMode'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: updateMemberAddModeSchema, + ClassRef: GroupUpdateMemberAddModeDto, + execute: (instance, data) => groupController.updateMemberAddMode(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) .post(this.routerPath('toggleEphemeral'), ...guards, async (req, res) => { const response = await this.groupValidate({ request: req, diff --git a/src/validate/group.schema.ts b/src/validate/group.schema.ts index 8da188d87..2962f1be0 100644 --- a/src/validate/group.schema.ts +++ b/src/validate/group.schema.ts @@ -191,3 +191,17 @@ export const updateGroupDescriptionSchema: JSONSchema7 = { required: ['groupJid', 'description'], ...isNotEmpty('groupJid', 'description'), }; + +export const updateMemberAddModeSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + groupJid: { type: 'string' }, + mode: { + type: 'string', + enum: ['admin_add', 'all_member_add'], + }, + }, + required: ['groupJid', 'mode'], + ...isNotEmpty('groupJid', 'mode'), +}; From 23eac3452ad5b6f548812c7493f42c82f4037760 Mon Sep 17 00:00:00 2001 From: hassankaid Date: Wed, 29 Apr 2026 17:09:46 +0100 Subject: [PATCH 2/2] ci: build & publish fork Docker image to GHCR Triggered on every push to feat/group-member-add-mode. Produces: ghcr.io/hassankaid/evolution-api:member-add-mode ghcr.io/hassankaid/evolution-api:sha- Allows users running self-hosted Evolution to swap their Docker image to this fork while keeping all their state (sessions, DB, Redis) untouched, until the upstream PR #2525 is merged. --- .github/workflows/publish-fork-image.yml | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/publish-fork-image.yml diff --git a/.github/workflows/publish-fork-image.yml b/.github/workflows/publish-fork-image.yml new file mode 100644 index 000000000..afbcdafff --- /dev/null +++ b/.github/workflows/publish-fork-image.yml @@ -0,0 +1,48 @@ +name: Publish fork Docker image + +on: + push: + branches: + - 'feat/group-member-add-mode' + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Compute lowercase repo + id: vars + run: echo "repo=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64 + tags: | + ghcr.io/${{ steps.vars.outputs.repo }}:member-add-mode + ghcr.io/${{ steps.vars.outputs.repo }}:sha-${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max