From 5ebc66d5a8c56a695c7cf6580db3c4ce6de04cb8 Mon Sep 17 00:00:00 2001 From: Alpes Digital Date: Sat, 20 Jun 2026 15:12:40 -0300 Subject: [PATCH 1/6] feat(group): member-add-mode, join-approval-mode, participant requests + message forward MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exposes Baileys group/message capabilities that already exist in the underlying library but had no REST endpoints: - POST /group/memberAddMode -> groupMemberAddMode (admin_add | all_member_add) - POST /group/joinApprovalMode -> groupJoinApprovalMode (on | off) - GET /group/participantRequests -> groupRequestParticipantsList (pending join requests) - POST /group/updateParticipantRequests -> groupRequestParticipantsUpdate (approve | reject) - POST /message/forwardMessage -> sendMessage(jid, { forward }) (keeps isForwarded) Each follows the existing pattern (DTO + JSONSchema + router + controller + baileys service). No new dependencies — baileys 7.0.0-rc.9 already ships these. Co-Authored-By: Claude Opus 4.8 --- src/api/controllers/group.controller.ts | 19 +++++++ src/api/controllers/sendMessage.controller.ts | 5 ++ src/api/dto/group.dto.ts | 13 +++++ src/api/dto/sendMessage.dto.ts | 4 ++ .../whatsapp/whatsapp.baileys.service.ts | 55 +++++++++++++++++++ src/api/routes/group.router.ts | 46 ++++++++++++++++ src/api/routes/sendMessage.router.ts | 12 ++++ src/validate/group.schema.ts | 39 +++++++++++++ src/validate/message.schema.ts | 11 ++++ 9 files changed, 204 insertions(+) diff --git a/src/api/controllers/group.controller.ts b/src/api/controllers/group.controller.ts index ebe7c03671..f2e9a2eb8e 100644 --- a/src/api/controllers/group.controller.ts +++ b/src/api/controllers/group.controller.ts @@ -5,11 +5,14 @@ import { GroupDescriptionDto, GroupInvite, GroupJid, + GroupJoinApprovalModeDto, + GroupMemberAddModeDto, GroupPictureDto, GroupSendInvite, GroupSubjectDto, GroupToggleEphemeralDto, GroupUpdateParticipantDto, + GroupUpdateParticipantRequestDto, GroupUpdateSettingDto, } from '@api/dto/group.dto'; import { InstanceDto } from '@api/dto/instance.dto'; @@ -78,6 +81,22 @@ export class GroupController { return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(update); } + public async updateMemberAddMode(instance: InstanceDto, update: GroupMemberAddModeDto) { + return await this.waMonitor.waInstances[instance.instanceName].updateMemberAddMode(update); + } + + public async updateJoinApprovalMode(instance: InstanceDto, update: GroupJoinApprovalModeDto) { + return await this.waMonitor.waInstances[instance.instanceName].updateJoinApprovalMode(update); + } + + public async findParticipantRequests(instance: InstanceDto, groupJid: GroupJid) { + return await this.waMonitor.waInstances[instance.instanceName].findParticipantRequests(groupJid); + } + + public async updateParticipantRequests(instance: InstanceDto, update: GroupUpdateParticipantRequestDto) { + return await this.waMonitor.waInstances[instance.instanceName].updateParticipantRequests(update); + } + public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) { return await this.waMonitor.waInstances[instance.instanceName].leaveGroup(groupJid); } diff --git a/src/api/controllers/sendMessage.controller.ts b/src/api/controllers/sendMessage.controller.ts index 64aa1c8468..68c55b0a73 100644 --- a/src/api/controllers/sendMessage.controller.ts +++ b/src/api/controllers/sendMessage.controller.ts @@ -1,5 +1,6 @@ import { InstanceDto } from '@api/dto/instance.dto'; import { + ForwardMessageDto, SendAudioDto, SendButtonsDto, SendContactDto, @@ -39,6 +40,10 @@ export class SendMessageController { return await this.waMonitor.waInstances[instanceName].textMessage(data); } + public async forwardMessage({ instanceName }: InstanceDto, data: ForwardMessageDto) { + return await this.waMonitor.waInstances[instanceName].forwardMessage(data); + } + public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto, file?: any) { if (isBase64(data?.media) && !data?.fileName && data?.mediatype === 'document') { throw new BadRequestException('For base64 the file name must be informed.'); diff --git a/src/api/dto/group.dto.ts b/src/api/dto/group.dto.ts index 293329d2ed..b9975d3f12 100644 --- a/src/api/dto/group.dto.ts +++ b/src/api/dto/group.dto.ts @@ -54,3 +54,16 @@ export class GroupUpdateSettingDto extends GroupJid { export class GroupToggleEphemeralDto extends GroupJid { expiration: 0 | 86400 | 604800 | 7776000; } + +export class GroupMemberAddModeDto extends GroupJid { + mode: 'admin_add' | 'all_member_add'; +} + +export class GroupJoinApprovalModeDto extends GroupJid { + mode: 'on' | 'off'; +} + +export class GroupUpdateParticipantRequestDto extends GroupJid { + action: 'approve' | 'reject'; + participants: string[]; +} diff --git a/src/api/dto/sendMessage.dto.ts b/src/api/dto/sendMessage.dto.ts index ba9ecf527c..1ba725dfbb 100644 --- a/src/api/dto/sendMessage.dto.ts +++ b/src/api/dto/sendMessage.dto.ts @@ -50,6 +50,10 @@ export class Metadata { export class SendTextDto extends Metadata { text: string; } +export class ForwardMessageDto { + number: string; + messageId: string; +} export class SendPresence extends Metadata { text: string; } diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 60e857fcc1..e717544420 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -22,11 +22,14 @@ import { GroupDescriptionDto, GroupInvite, GroupJid, + GroupJoinApprovalModeDto, + GroupMemberAddModeDto, GroupPictureDto, GroupSendInvite, GroupSubjectDto, GroupToggleEphemeralDto, GroupUpdateParticipantDto, + GroupUpdateParticipantRequestDto, GroupUpdateSettingDto, } from '@api/dto/group.dto'; import { InstanceDto, SetPresenceDto } from '@api/dto/instance.dto'; @@ -34,6 +37,7 @@ import { HandleLabelDto, LabelDto } from '@api/dto/label.dto'; import { Button, ContactMessage, + ForwardMessageDto, KeyType, MediaMessage, Options, @@ -553,6 +557,20 @@ export class BaileysStartupService extends ChannelStartupService { } } + public async forwardMessage(data: ForwardMessageDto) { + try { + const fullMsg = (await this.getMessage({ id: data.messageId }, true)) as unknown as WAMessage; + if (!fullMsg?.message) { + throw new BadRequestException('Message not found'); + } + const number = data.number.replace(/\D/g, ''); + const jid = data.number.includes('@') ? data.number : `${number}@s.whatsapp.net`; + return await this.client.sendMessage(jid, { forward: fullMsg }); + } catch (error) { + throw new BadRequestException('Error forwarding message', error.toString()); + } + } + private async defineAuthState() { const db = this.configService.get('DATABASE'); const cache = this.configService.get('CACHE'); @@ -4597,6 +4615,43 @@ export class BaileysStartupService extends ChannelStartupService { } } + public async updateMemberAddMode(update: GroupMemberAddModeDto) { + try { + await this.client.groupMemberAddMode(update.groupJid, update.mode); + return { success: true }; + } catch (error) { + throw new BadRequestException('Error updating member add mode', error.toString()); + } + } + + public async updateJoinApprovalMode(update: GroupJoinApprovalModeDto) { + try { + await this.client.groupJoinApprovalMode(update.groupJid, update.mode); + return { success: true }; + } catch (error) { + throw new BadRequestException('Error updating join approval mode', error.toString()); + } + } + + public async findParticipantRequests(id: GroupJid) { + try { + const requests = await this.client.groupRequestParticipantsList(id.groupJid); + return { requests: requests || [] }; + } catch (error) { + throw new BadRequestException('Error fetching participant requests', error.toString()); + } + } + + public async updateParticipantRequests(update: GroupUpdateParticipantRequestDto) { + try { + const participants = update.participants.map((p) => (p.includes('@') ? p : `${p}@s.whatsapp.net`)); + const result = await this.client.groupRequestParticipantsUpdate(update.groupJid, participants, update.action); + return { updateParticipantRequests: result }; + } catch (error) { + throw new BadRequestException('Error updating participant requests', error.toString()); + } + } + public async leaveGroup(id: GroupJid) { try { await this.client.groupLeave(id.groupJid); diff --git a/src/api/routes/group.router.ts b/src/api/routes/group.router.ts index 7086b11769..c16d951d18 100644 --- a/src/api/routes/group.router.ts +++ b/src/api/routes/group.router.ts @@ -6,11 +6,14 @@ import { GroupDescriptionDto, GroupInvite, GroupJid, + GroupJoinApprovalModeDto, + GroupMemberAddModeDto, GroupPictureDto, GroupSendInvite, GroupSubjectDto, GroupToggleEphemeralDto, GroupUpdateParticipantDto, + GroupUpdateParticipantRequestDto, GroupUpdateSettingDto, } from '@api/dto/group.dto'; import { groupController } from '@api/server.module'; @@ -21,10 +24,13 @@ import { groupInviteSchema, groupJidSchema, groupSendInviteSchema, + joinApprovalModeSchema, + memberAddModeSchema, toggleEphemeralSchema, updateGroupDescriptionSchema, updateGroupPictureSchema, updateGroupSubjectSchema, + updateParticipantRequestSchema, updateParticipantsSchema, updateSettingsSchema, } from '@validate/validate.schema'; @@ -186,6 +192,46 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) + .post(this.routerPath('memberAddMode'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: memberAddModeSchema, + ClassRef: GroupMemberAddModeDto, + execute: (instance, data) => groupController.updateMemberAddMode(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('joinApprovalMode'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: joinApprovalModeSchema, + ClassRef: GroupJoinApprovalModeDto, + execute: (instance, data) => groupController.updateJoinApprovalMode(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) + .get(this.routerPath('participantRequests'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: groupJidSchema, + ClassRef: GroupJid, + execute: (instance, data) => groupController.findParticipantRequests(instance, data), + }); + + res.status(HttpStatus.OK).json(response); + }) + .post(this.routerPath('updateParticipantRequests'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: updateParticipantRequestSchema, + ClassRef: GroupUpdateParticipantRequestDto, + execute: (instance, data) => groupController.updateParticipantRequests(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) .delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => { const response = await this.groupValidate({ request: req, diff --git a/src/api/routes/sendMessage.router.ts b/src/api/routes/sendMessage.router.ts index cd073dba3d..dee71cbf08 100644 --- a/src/api/routes/sendMessage.router.ts +++ b/src/api/routes/sendMessage.router.ts @@ -1,5 +1,6 @@ import { RouterBroker } from '@api/abstract/abstract.router'; import { + ForwardMessageDto, SendAudioDto, SendButtonsDto, SendContactDto, @@ -19,6 +20,7 @@ import { audioMessageSchema, buttonsMessageSchema, contactMessageSchema, + forwardMessageSchema, listMessageSchema, locationMessageSchema, mediaMessageSchema, @@ -61,6 +63,16 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) + .post(this.routerPath('forwardMessage'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: forwardMessageSchema, + ClassRef: ForwardMessageDto, + execute: (instance, data) => sendMessageController.forwardMessage(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) .post(this.routerPath('sendMedia'), ...guards, upload.single('file'), async (req, res) => { const bodyData = req.body; diff --git a/src/validate/group.schema.ts b/src/validate/group.schema.ts index 8da188d87f..75db868953 100644 --- a/src/validate/group.schema.ts +++ b/src/validate/group.schema.ts @@ -159,6 +159,45 @@ export const toggleEphemeralSchema: JSONSchema7 = { ...isNotEmpty('groupJid', 'expiration'), }; +export const memberAddModeSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + groupJid: { type: 'string' }, + mode: { type: 'string', enum: ['admin_add', 'all_member_add'] }, + }, + required: ['groupJid', 'mode'], + ...isNotEmpty('groupJid', 'mode'), +}; + +export const joinApprovalModeSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + groupJid: { type: 'string' }, + mode: { type: 'string', enum: ['on', 'off'] }, + }, + required: ['groupJid', 'mode'], + ...isNotEmpty('groupJid', 'mode'), +}; + +export const updateParticipantRequestSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + groupJid: { type: 'string' }, + action: { type: 'string', enum: ['approve', 'reject'] }, + participants: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { type: 'string' }, + }, + }, + required: ['groupJid', 'action', 'participants'], + ...isNotEmpty('groupJid', 'action'), +}; + export const updateGroupPictureSchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/validate/message.schema.ts b/src/validate/message.schema.ts index d514c6199e..fcb138e4d0 100644 --- a/src/validate/message.schema.ts +++ b/src/validate/message.schema.ts @@ -65,6 +65,17 @@ export const offerCallSchema: JSONSchema7 = { required: ['number', 'callDuration'], }; +export const forwardMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + messageId: { type: 'string' }, + }, + required: ['number', 'messageId'], + ...isNotEmpty('number', 'messageId'), +}; + export const textMessageSchema: JSONSchema7 = { $id: v4(), type: 'object', From 3efa13ce549e3f356493f361ee38213a2ef4b19b Mon Sep 17 00:00:00 2001 From: root Date: Mon, 6 Jul 2026 23:40:17 +0000 Subject: [PATCH 2/6] fix(groups): throttle groupMetadata (evita 429 em contas com centenas de grupos) + baileys rc13 --- package-lock.json | 768 +++++++----------- package.json | 4 +- .../whatsapp/whatsapp.baileys.service.ts | 37 +- 3 files changed, 311 insertions(+), 498 deletions(-) diff --git a/package-lock.json b/package-lock.json index c45e8fef38..8eda484379 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "amqplib": "^0.10.5", "audio-decode": "^2.2.3", "axios": "^1.7.9", - "baileys": "7.0.0-rc.9", + "baileys": "^7.0.0-rc13", "class-validator": "^0.14.1", "compression": "^1.7.5", "cors": "^2.8.5", @@ -829,9 +829,9 @@ } }, "node_modules/@borewit/text-codec": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.0.tgz", - "integrity": "sha512-X999CKBxGwX8wW+4gFibsbiNdwqmdQEXmUejIWaIqdrHBgS5ARIOOeyiQbHjP9G58xVEPcuvP6VwwH3A0OFTOA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", "license": "MIT", "funding": { "type": "github", @@ -2372,17 +2372,17 @@ } }, "node_modules/@jimp/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", - "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.1.tgz", + "integrity": "sha512-+BoKC5G6hkrSy501zcJ2EpfnllP+avPevcBfRcZe/CW+EwEfY6X1EZ8QWyT7NpDIvEEJb1fdJnMMfUnFkxmw9A==", "license": "MIT", "dependencies": { - "@jimp/file-ops": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/file-ops": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", - "file-type": "^16.0.0", + "file-type": "^21.3.3", "mime": "3" }, "engines": { @@ -2402,14 +2402,14 @@ } }, "node_modules/@jimp/diff": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", - "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.1.tgz", + "integrity": "sha512-YkKDPdHjLgo1Api3+Bhc0GLAygldlpt97NfOKoNg1U6IUNXA6X2MgosCjPfSBiSvJvrrz1fsIR+/4cfYXBI/HQ==", "license": "MIT", "dependencies": { - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "pixelmatch": "^5.3.0" }, "engines": { @@ -2417,23 +2417,23 @@ } }, "node_modules/@jimp/file-ops": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", - "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.1.tgz", + "integrity": "sha512-T+gX6osHjprbDRad0/B71Evyre7ZdVY1z/gFGEG9Z8KOtZPKboWvPeP2UjbZYWQLy9UKCPQX1FNAnDiOPkJL7w==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@jimp/js-bmp": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", - "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.1.tgz", + "integrity": "sha512-xzWzNT4/u5zGrTT3Tme9sGU7YzIKxi13+BCQwLqACbt5DXf9SAfdzRkopZQnmDko+6In5nqaT89Gjs43/WdnYQ==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "bmp-ts": "^1.0.9" }, "engines": { @@ -2441,13 +2441,13 @@ } }, "node_modules/@jimp/js-gif": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", - "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.1.tgz", + "integrity": "sha512-YjY2W26rQa05XhanYhRZ7dingCiNN+T2Ymb1JiigIbABY0B28wHE3v3Cf1/HZPWGu0hOg36ylaKgV5KxF2M58w==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "gifwrap": "^0.10.1", "omggif": "^1.0.10" }, @@ -2456,13 +2456,13 @@ } }, "node_modules/@jimp/js-jpeg": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", - "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.1.tgz", + "integrity": "sha512-HT9H3yOmlOFzYmdI15IYdfy6ggQhSRIaHeA+OTJSEORXBqEo97sUZu/DsgHIcX5NJ7TkJBTgZ9BZXsV6UbsyMg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "jpeg-js": "^0.4.4" }, "engines": { @@ -2470,13 +2470,13 @@ } }, "node_modules/@jimp/js-png": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", - "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.1.tgz", + "integrity": "sha512-SZ/KVhI5UjcSzzlXsXdIi/LhJ7UShf2NkMOtVrbZQcGzsqNtynAelrOXeoTxcanfVqmNhAoVHg8yR2cYoqrYjA==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "pngjs": "^7.0.0" }, "engines": { @@ -2484,13 +2484,13 @@ } }, "node_modules/@jimp/js-tiff": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", - "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.1.tgz", + "integrity": "sha512-jDG/eJquID1M4MBlKMmDRBmz2TpXMv7TUyu2nIRUxhlUc2ogC82T+VQUkca9GJH1BBJ9dx5sSE5dGkWNjIbZxw==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "utif2": "^4.1.0" }, "engines": { @@ -2498,13 +2498,13 @@ } }, "node_modules/@jimp/plugin-blit": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", - "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.1.tgz", + "integrity": "sha512-MwnI7C7K81uWddY9FLw1fCOIy6SsPIUftUz36Spt7jisCn8/40DhQMlSxpxTNelnZb/2SnloFimQfRZAmHLOqQ==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2512,25 +2512,25 @@ } }, "node_modules/@jimp/plugin-blur": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", - "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.1.tgz", + "integrity": "sha512-lIo7Tzp5jQu30EFFSK/phXANK3citKVEjepDjQ6ljHoIFtuMRrnybnmI2Md24ulvWlDaz+hh3n6qrMb8ydwhZQ==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/utils": "1.6.0" + "@jimp/core": "1.6.1", + "@jimp/utils": "1.6.1" }, "engines": { "node": ">=18" } }, "node_modules/@jimp/plugin-circle": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", - "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.1.tgz", + "integrity": "sha512-kK1PavY6cKHNNKce37vdV4Tmpc1/zDKngGoeOV3j+EMatoHFZUinV3s6F9aWryPs3A0xhCLZgdJ6Zeea1d5LCQ==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2538,14 +2538,14 @@ } }, "node_modules/@jimp/plugin-color": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", - "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.1.tgz", + "integrity": "sha512-LtUN1vAP+LRlZAtTNVhDRSiXx+26Kbz3zJaG6a5k59gQ95jgT5mknnF8lxkHcqJthM4MEk3/tPxkdJpEybyF/A==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "tinycolor2": "^1.6.0", "zod": "^3.23.8" }, @@ -2554,16 +2554,16 @@ } }, "node_modules/@jimp/plugin-contain": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", - "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.1.tgz", + "integrity": "sha512-m0qhrfA8jkTqretGv4w+T/ADFR4GwBpE0sCOC2uJ0dzr44/ddOMsIdrpi89kabqYiPYIrxkgdCVCLm3zn1Vkkg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-blit": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2571,15 +2571,15 @@ } }, "node_modules/@jimp/plugin-cover": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", - "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.1.tgz", + "integrity": "sha512-hZytnsth0zoll6cPf434BrT+p/v569Wr5tyO6Dp0dH1IDPhzhB5F38sZGMLDo7bzQiN9JFVB3fxkcJ/WYCJ3Mg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-crop": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2587,14 +2587,14 @@ } }, "node_modules/@jimp/plugin-crop": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", - "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.1.tgz", + "integrity": "sha512-EerRSLlclXyKDnYc/H9w/1amZW7b7v3OGi/VlerPd2M/pAu5X8TkyYWtfqYCXnNp1Ixtd8oCo9zGfY9zoXT4rg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2602,13 +2602,13 @@ } }, "node_modules/@jimp/plugin-displace": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", - "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.1.tgz", + "integrity": "sha512-K07QVl7xQwIfD6KfxRV/c3E9e7ZBXxUXdWuvoTWcKHL2qV48MOF5Nqbz/aJW4ThnQARIsxvYlZjPFiqkCjlU+g==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2616,25 +2616,25 @@ } }, "node_modules/@jimp/plugin-dither": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", - "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.1.tgz", + "integrity": "sha512-+2V+GCV2WycMoX1/z977TkZ8Zq/4MVSKElHYatgUqtwXMi2fDK2gKYU2g9V39IqFvTJsTIsK0+58VFz/ROBVew==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0" + "@jimp/types": "1.6.1" }, "engines": { "node": ">=18" } }, "node_modules/@jimp/plugin-fisheye": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", - "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.1.tgz", + "integrity": "sha512-XtS5ZyoZ0vxZxJ6gkqI63SivhtI58vX95foMPM+cyzYkRsJXMOYCr8DScxF5bp4Xr003NjYm/P+7+08tibwzHA==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2642,12 +2642,12 @@ } }, "node_modules/@jimp/plugin-flip": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", - "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.1.tgz", + "integrity": "sha512-ws38W/sGj7LobNRayQ83garxiktOyWxM5vO/y4a/2cy9v65SLEUzVkrj+oeAaUSSObdz4HcCEla7XtGlnAGAaA==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2655,20 +2655,20 @@ } }, "node_modules/@jimp/plugin-hash": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", - "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", - "license": "MIT", - "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/js-bmp": "1.6.0", - "@jimp/js-jpeg": "1.6.0", - "@jimp/js-png": "1.6.0", - "@jimp/js-tiff": "1.6.0", - "@jimp/plugin-color": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.1.tgz", + "integrity": "sha512-sZt6ZcMX6i8vFWb4GYnw0pR/o9++ef0dTVcboTB5B/g7nrxCODIB4wfEkJ/YqZM5wUvol77K1qeS0/rVO6z21A==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/js-bmp": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/js-tiff": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "any-base": "^1.1.0" }, "engines": { @@ -2676,12 +2676,12 @@ } }, "node_modules/@jimp/plugin-mask": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", - "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.1.tgz", + "integrity": "sha512-SIG0/FcmEj3tkwFxc7fAGLO8o4uNzMpSOdQOhbCgxefQKq5wOVMk9BQx/sdMPBwtMLr9WLq0GzLA/rk6t2v20A==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2689,16 +2689,16 @@ } }, "node_modules/@jimp/plugin-print": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", - "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.1.tgz", + "integrity": "sha512-BYVz/X3Xzv8XYilVeDy11NOp0h7BTDjlOtu0BekIFHP1yHVd24AXNzbOy52XlzYZWQ0Dl36HOHEpl/nSNrzc6w==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/js-jpeg": "1.6.0", - "@jimp/js-png": "1.6.0", - "@jimp/plugin-blit": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/types": "1.6.1", "parse-bmfont-ascii": "^1.0.6", "parse-bmfont-binary": "^1.0.6", "parse-bmfont-xml": "^1.1.6", @@ -2710,9 +2710,9 @@ } }, "node_modules/@jimp/plugin-quantize": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", - "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.1.tgz", + "integrity": "sha512-J2En9PLURfP+vwYDtuZ9T8yBW6BWYZBScydAjRiPBmJfEhTcNQqiiQODrZf7EqbbX/Sy5H6dAeRiqkgoV9N6Ww==", "license": "MIT", "dependencies": { "image-q": "^4.0.0", @@ -2723,13 +2723,13 @@ } }, "node_modules/@jimp/plugin-resize": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", - "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.1.tgz", + "integrity": "sha512-CLkrtJoIz2HdWnpYiN6p8KYcPc00rCH/SUu6o+lfZL05Q4uhecJlnvXuj9x+U6mDn3ldPmJj6aZqMHuUJzdVqg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2737,16 +2737,16 @@ } }, "node_modules/@jimp/plugin-rotate": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", - "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.1.tgz", + "integrity": "sha512-nOjVjbbj705B02ksysKnh0POAwEBXZtJ9zQ5qC+X7Tavl3JNn+P3BzQovbBxLPSbUSld6XID9z5ijin4PtOAUg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-crop": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2754,16 +2754,16 @@ } }, "node_modules/@jimp/plugin-threshold": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", - "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.1.tgz", + "integrity": "sha512-JOKv9F8s6tnVLf4sB/2fF0F339EFnHvgEdFYugO6VhowKLsap0pEZmLyE/DlRnYtIj2RddHZVxVMp/eKJ04l2Q==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-color": "1.6.0", - "@jimp/plugin-hash": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-hash": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2771,9 +2771,9 @@ } }, "node_modules/@jimp/types": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", - "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.1.tgz", + "integrity": "sha512-leI7YbveTNi565m910XgIOwXyuu074H5qazAD1357HImJSv2hqxnWXpwxQbadGWZ7goZRYBDZy5lpqud0p7q5w==", "license": "MIT", "dependencies": { "zod": "^3.23.8" @@ -2783,12 +2783,12 @@ } }, "node_modules/@jimp/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-veFPRd93FCnS7AgmCkPgARVGoDRrJ9cm1ujuNyA+UfQ5VKbED2002sm5XfFLFwTsKC8j04heTrwe+tU1dluXOw==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "tinycolor2": "^1.6.0" }, "engines": { @@ -3578,25 +3578,24 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "license": "BSD-3-Clause", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@protobufjs/aspromise": "^1.1.1" } }, "node_modules/@protobufjs/float": { @@ -3605,12 +3604,6 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", @@ -3624,9 +3617,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", "license": "BSD-3-Clause" }, "node_modules/@redis/bloom": { @@ -4758,34 +4751,6 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/@tokenizer/inflate/node_modules/@borewit/text-codec": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", - "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@tokenizer/inflate/node_modules/token-types": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", - "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", - "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.1.0", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", @@ -4895,12 +4860,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, "node_modules/@types/mime": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-4.0.0.tgz", @@ -5814,21 +5773,22 @@ } }, "node_modules/baileys": { - "version": "7.0.0-rc.9", - "resolved": "https://registry.npmjs.org/baileys/-/baileys-7.0.0-rc.9.tgz", - "integrity": "sha512-Txd2dZ9MHbojvsHckeuCnAKPO/bQjKxua/0tQSJwOKXffK5vpS82k4eA/Nb46K0cK0Bx+fyY0zhnQHYMBriQcw==", + "version": "7.0.0-rc13", + "resolved": "https://registry.npmjs.org/baileys/-/baileys-7.0.0-rc13.tgz", + "integrity": "sha512-v8k74K8B5R7WNYGa26MyJAYEu3Wc4BSuK01QaK8lr30lhE8Nga31nWNu8KN0NDDt+Fsvkq4SQFFI8Q13ghjKmA==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@cacheable/node-cache": "^1.4.0", "@hapi/boom": "^9.1.3", "async-mutex": "^0.5.0", - "libsignal": "git+https://github.com/whiskeysockets/libsignal-node.git", + "libsignal": "^6.0.0", "lru-cache": "^11.1.0", - "music-metadata": "^11.7.0", + "music-metadata": "^11.12.3", "p-queue": "^9.0.0", "pino": "^9.6", - "protobufjs": "^7.2.4", + "protobufjs": "^7.5.6", + "whatsapp-rust-bridge": "0.5.4", "ws": "^8.13.0" }, "engines": { @@ -5836,7 +5796,7 @@ }, "peerDependencies": { "audio-decode": "^2.1.3", - "jimp": "^1.6.0", + "jimp": "^1.6.1", "link-preview-js": "^3.0.0", "sharp": "*" }, @@ -5877,6 +5837,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, "funding": [ { "type": "github", @@ -8336,15 +8297,6 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "license": "MIT" }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/exif-parser": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", @@ -8629,17 +8581,18 @@ } }, "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", "license": "MIT", "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" @@ -10351,38 +10304,38 @@ "license": "ISC" }, "node_modules/jimp": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", - "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", - "license": "MIT", - "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/diff": "1.6.0", - "@jimp/js-bmp": "1.6.0", - "@jimp/js-gif": "1.6.0", - "@jimp/js-jpeg": "1.6.0", - "@jimp/js-png": "1.6.0", - "@jimp/js-tiff": "1.6.0", - "@jimp/plugin-blit": "1.6.0", - "@jimp/plugin-blur": "1.6.0", - "@jimp/plugin-circle": "1.6.0", - "@jimp/plugin-color": "1.6.0", - "@jimp/plugin-contain": "1.6.0", - "@jimp/plugin-cover": "1.6.0", - "@jimp/plugin-crop": "1.6.0", - "@jimp/plugin-displace": "1.6.0", - "@jimp/plugin-dither": "1.6.0", - "@jimp/plugin-fisheye": "1.6.0", - "@jimp/plugin-flip": "1.6.0", - "@jimp/plugin-hash": "1.6.0", - "@jimp/plugin-mask": "1.6.0", - "@jimp/plugin-print": "1.6.0", - "@jimp/plugin-quantize": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/plugin-rotate": "1.6.0", - "@jimp/plugin-threshold": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.1.tgz", + "integrity": "sha512-hNQh6rZtWfSVWSNVmvq87N5BPJsNH7k7I7qyrXf9DOma9xATQk3fsyHazCQe51nCjdkoWdTmh0vD7bjVSLoxxw==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/diff": "1.6.1", + "@jimp/js-bmp": "1.6.1", + "@jimp/js-gif": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/js-tiff": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/plugin-blur": "1.6.1", + "@jimp/plugin-circle": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-contain": "1.6.1", + "@jimp/plugin-cover": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-displace": "1.6.1", + "@jimp/plugin-dither": "1.6.1", + "@jimp/plugin-fisheye": "1.6.1", + "@jimp/plugin-flip": "1.6.1", + "@jimp/plugin-hash": "1.6.1", + "@jimp/plugin-mask": "1.6.1", + "@jimp/plugin-print": "1.6.1", + "@jimp/plugin-quantize": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/plugin-rotate": "1.6.1", + "@jimp/plugin-threshold": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1" }, "engines": { "node": ">=18" @@ -10610,51 +10563,13 @@ "license": "MIT" }, "node_modules/libsignal": { - "name": "@whiskeysockets/libsignal-node", - "version": "2.0.1", - "resolved": "git+ssh://git@github.com/whiskeysockets/libsignal-node.git#1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/libsignal/-/libsignal-6.0.0.tgz", + "integrity": "sha512-d/5V3YFtDljbFMufz4ncyUYGYhJl+vzAe+c2EFFBQ6bz1h8Q3IOMEGXYMzlibU60I+e8GagMMpji18iez3P1hA==", "license": "GPL-3.0", "dependencies": { "curve25519-js": "^0.0.4", - "protobufjs": "6.8.8" - } - }, - "node_modules/libsignal/node_modules/@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "license": "MIT" - }, - "node_modules/libsignal/node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" - }, - "node_modules/libsignal/node_modules/protobufjs": { - "version": "6.8.8", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.0", - "@types/node": "^10.1.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" + "protobufjs": "^7.5.5" } }, "node_modules/lilconfig": { @@ -11232,12 +11147,16 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-2.0.0.tgz", + "integrity": "sha512-kOy3OxT2HH39N70UnKgu4NWDZjLOz8W/mfyvniHjRH/DrL3f2pOfvWQ4p60offbbtDAnXWp0v9LfMIqMec269Q==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mediainfo.js": { @@ -11656,9 +11575,9 @@ } }, "node_modules/music-metadata": { - "version": "11.10.3", - "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.10.3.tgz", - "integrity": "sha512-j0g/x4cNNZW6I5gdcPAY+GFkJY9WHTpkFDMBJKQLxJQyvSfQbXm57fTE3haGFFuOzCgtsTd4Plwc49Sn9RacDQ==", + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.13.0.tgz", + "integrity": "sha512-uXRaov9dfjSpQufXIU7sMxVZnh+FilCQv2mXn+K5EJ/decP3dTWrgvPYa5r6MtRbieNSCE708Da4J0u1UGfQIw==", "funding": [ { "type": "github", @@ -11671,80 +11590,32 @@ ], "license": "MIT", "dependencies": { - "@borewit/text-codec": "^0.2.0", + "@borewit/text-codec": "^0.2.2", "@tokenizer/token": "^0.3.0", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "file-type": "^21.1.1", - "media-typer": "^1.1.0", - "strtok3": "^10.3.4", - "token-types": "^6.1.1", - "uint8array-extras": "^1.5.0" + "file-type": "^21.3.4", + "media-typer": "^2.0.0", + "strtok3": "^10.3.5", + "token-types": "^6.1.2", + "uint8array-extras": "^1.5.0", + "win-guid": "^0.2.1" }, "engines": { "node": ">=18" } }, - "node_modules/music-metadata/node_modules/file-type": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.1.1.tgz", - "integrity": "sha512-ifJXo8zUqbQ/bLbl9sFoqHNTNWbnPY1COImFfM6CCy7z+E+jC1eY9YfOKkx0fckIg+VljAy2/87T61fp0+eEkg==", - "license": "MIT", - "dependencies": { - "@tokenizer/inflate": "^0.4.1", - "strtok3": "^10.3.4", - "token-types": "^6.1.1", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/music-metadata/node_modules/strtok3": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", - "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "node_modules/music-metadata/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0" - }, "engines": { "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/music-metadata/node_modules/token-types": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", - "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", - "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.1.0", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/music-metadata/node_modules/token-types/node_modules/@borewit/text-codec": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", - "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mute-stream": { @@ -12576,19 +12447,6 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -12957,15 +12815,6 @@ } } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-warning": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", @@ -12983,24 +12832,23 @@ "license": "MIT" }, "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", - "long": "^5.0.0" + "long": "^5.3.2" }, "engines": { "node": ">=12.0.0" @@ -13411,62 +13259,6 @@ "node": ">= 6" } }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", - "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", - "license": "MIT", - "dependencies": { - "readable-stream": "^4.7.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/readable-web-to-node-stream/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -14176,9 +13968,9 @@ "license": "ISC" }, "node_modules/simple-xml-to-json": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.3.tgz", - "integrity": "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.7.tgz", + "integrity": "sha512-mz9VXphOxQWX3eQ/uXCtm6upltoN0DLx8Zb5T4TFC4FHB7S9FDPGre8CfLWqPWQQH/GrQYd2AXhhVM5LDpYx6Q==", "license": "MIT", "engines": { "node": ">=20.12.2" @@ -14640,16 +14432,15 @@ "license": "MIT" }, "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" + "@tokenizer/token": "^0.3.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "type": "github", @@ -14914,16 +14705,17 @@ } }, "node_modules/token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", "license": "MIT", "dependencies": { + "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "type": "github", @@ -15944,6 +15736,12 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, + "node_modules/whatsapp-rust-bridge": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/whatsapp-rust-bridge/-/whatsapp-rust-bridge-0.5.4.tgz", + "integrity": "sha512-yYO1qSs0Fe7tGtnxOFHomocUD6IZtoAgmA4oDFyGIRZ67D3QZk3w7swA6XXFXNQngiyrg2k7tul6IrM3eUFh7A==", + "license": "MIT" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -16064,6 +15862,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/win-guid": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/win-guid/-/win-guid-0.2.1.tgz", + "integrity": "sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==", + "license": "MIT" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 56e32fcc8a..7fb7f03b51 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "amqplib": "^0.10.5", "audio-decode": "^2.2.3", "axios": "^1.7.9", - "baileys": "7.0.0-rc.9", + "baileys": "^7.0.0-rc13", "class-validator": "^0.14.1", "compression": "^1.7.5", "cors": "^2.8.5", @@ -87,10 +87,10 @@ "eventemitter2": "^6.4.9", "express": "^4.21.2", "express-async-errors": "^3.1.1", + "fetch-socks": "^1.3.2", "fluent-ffmpeg": "^2.1.3", "form-data": "^4.0.1", "https-proxy-agent": "^7.0.6", - "fetch-socks": "^1.3.2", "i18next": "^23.7.19", "jimp": "^1.6.0", "json-schema": "^0.4.0", diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index e717544420..be97c90a01 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -228,6 +228,12 @@ async function getVideoDuration(input: Buffer | string | Readable): Promise = Promise.resolve(); +let __groupMetaLastAt = 0; +const __GROUP_META_MIN_INTERVAL_MS = 2000; + export class BaileysStartupService extends ChannelStartupService { private messageProcessor = new BaileysMessageProcessor(); @@ -4303,21 +4309,24 @@ export class BaileysStartupService extends ChannelStartupService { // Group private async updateGroupMetadataCache(groupJid: string) { - try { - const meta = await this.client.groupMetadata(groupJid); - - const cacheConf = this.configService.get('CACHE'); - - if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) { - this.logger.verbose(`Updating cache for group: ${groupJid}`); - await groupMetadataCache.set(groupJid, { timestamp: Date.now(), data: meta }); + const task = __groupMetaChain.then(async () => { + const wait = __GROUP_META_MIN_INTERVAL_MS - (Date.now() - __groupMetaLastAt); + if (wait > 0) await new Promise((r) => setTimeout(r, wait)); + __groupMetaLastAt = Date.now(); + try { + const meta = await this.client.groupMetadata(groupJid); + const cacheConf = this.configService.get('CACHE'); + if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) { + await groupMetadataCache.set(groupJid, { timestamp: Date.now(), data: meta }); + } + return meta; + } catch (error) { + this.logger.error(error); + return null; } - - return meta; - } catch (error) { - this.logger.error(error); - return null; - } + }); + __groupMetaChain = task.then(() => undefined, () => undefined); + return task; } private getGroupMetadataCache = async (groupJid: string) => { From 6d3ed8a9c622e6863c74f8a767c201779c5bbb1d Mon Sep 17 00:00:00 2001 From: root Date: Mon, 6 Jul 2026 23:58:24 +0000 Subject: [PATCH 3/6] =?UTF-8?q?feat(groups):=20modo=20lite=20no=20fetchAll?= =?UTF-8?q?Groups=20(getParticipants=3Dfalse=20pula=20profilePicture=20?= =?UTF-8?q?=E2=80=94=20listagem=20sem=20rajada)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integrations/channel/whatsapp/whatsapp.baileys.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index be97c90a01..7f37a4ed9b 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -4477,7 +4477,8 @@ export class BaileysStartupService extends ChannelStartupService { let groups = []; for (const group of fetch) { - const picture = await this.profilePicture(group.id); + const wantDetails = getParticipants.getParticipants == 'true'; + const picture = wantDetails ? await this.profilePicture(group.id) : null; const result = { id: group.id, From d771f48c307e386263d05f91a424adcd30307817 Mon Sep 17 00:00:00 2001 From: alpesdigital-adm Date: Wed, 9 Sep 2026 09:21:45 -0300 Subject: [PATCH 4/6] fix(groups): expose observed group metadata settings (#1) Co-authored-by: root --- .../whatsapp/whatsapp.baileys.service.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 7f37a4ed9b..010e5deb8d 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -4459,6 +4459,15 @@ export class BaileysStartupService extends ChannelStartupService { descId: group.descId, restrict: group.restrict, announce: group.announce, + joinApprovalMode: group.joinApprovalMode, + memberAddMode: group.memberAddMode, + // In complete Baileys metadata, an absent ephemeral node means disabled. + ephemeralDuration: + typeof group.joinApprovalMode === 'boolean' && typeof group.memberAddMode === 'boolean' + ? group.ephemeralDuration === undefined + ? 0 + : group.ephemeralDuration + : group.ephemeralDuration, participants: group.participants, isCommunity: group.isCommunity, isCommunityAnnounce: group.isCommunityAnnounce, @@ -4493,6 +4502,15 @@ export class BaileysStartupService extends ChannelStartupService { descId: group.descId, restrict: group.restrict, announce: group.announce, + joinApprovalMode: group.joinApprovalMode, + memberAddMode: group.memberAddMode, + // In complete Baileys metadata, an absent ephemeral node means disabled. + ephemeralDuration: + typeof group.joinApprovalMode === 'boolean' && typeof group.memberAddMode === 'boolean' + ? group.ephemeralDuration === undefined + ? 0 + : group.ephemeralDuration + : group.ephemeralDuration, isCommunity: group.isCommunity, isCommunityAnnounce: group.isCommunityAnnounce, linkedParent: group.linkedParent, From ab7854382b201b9a3f91a69f19c274b2b22544ef Mon Sep 17 00:00:00 2001 From: Alpes Digital Date: Mon, 28 Sep 2026 14:58:55 -0300 Subject: [PATCH 5/6] feat(chat): synchronize linked-device read state --- .github/workflows/release-read-state.yml | 72 +++++++++++ src/api/dto/chat.dto.ts | 1 + .../channel/whatsapp/read-state.ts | 74 +++++++++++ .../whatsapp/whatsapp.baileys.service.ts | 25 ++-- src/validate/chat.schema.ts | 20 ++- test/read-state.test.ts | 117 ++++++++++++++++++ 6 files changed, 294 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/release-read-state.yml create mode 100644 src/api/integrations/channel/whatsapp/read-state.ts create mode 100644 test/read-state.test.ts diff --git a/.github/workflows/release-read-state.yml b/.github/workflows/release-read-state.yml new file mode 100644 index 0000000000..97bb2986a3 --- /dev/null +++ b/.github/workflows/release-read-state.yml @@ -0,0 +1,72 @@ +name: Read-state release candidate + +on: + push: + branches: + - codex/read-state-sync + - feat/group-admin-controls-and-forward + workflow_dispatch: + +permissions: + contents: read + +jobs: + release-image: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + submodules: recursive + + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + node-version: '24' + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Generate Prisma client + run: npm run db:generate + env: + DATABASE_PROVIDER: postgresql + + - name: Test read-state contract + run: node --import tsx --test test/read-state.test.ts + + - name: Check code quality + run: npm run lint:check + + - name: Build application + run: npm run build + + - name: Set image names + id: image + shell: bash + run: | + echo "tag=evolution-api-fork:release-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + echo "archive=evolution-api-fork-release-${GITHUB_SHA}.tar.gz" >> "$GITHUB_OUTPUT" + + - name: Build Docker candidate + run: docker build --tag "${{ steps.image.outputs.tag }}" . + + - name: Export image and checksum + shell: bash + run: | + set -euo pipefail + docker save "${{ steps.image.outputs.tag }}" | gzip -1 > "${{ steps.image.outputs.archive }}" + sha256sum "${{ steps.image.outputs.archive }}" > "${{ steps.image.outputs.archive }}.sha256" + + - name: Upload release candidate + uses: actions/upload-artifact@v4 + with: + name: release-image-${{ github.sha }} + path: | + ${{ steps.image.outputs.archive }} + ${{ steps.image.outputs.archive }}.sha256 + if-no-files-found: error + retention-days: 1 + compression-level: 0 diff --git a/src/api/dto/chat.dto.ts b/src/api/dto/chat.dto.ts index b11f32b054..0c12071956 100644 --- a/src/api/dto/chat.dto.ts +++ b/src/api/dto/chat.dto.ts @@ -68,6 +68,7 @@ class Key { } export class ReadMessageDto { readMessages: Key[]; + lastMessage?: { key: Key; messageTimestamp: number }; } export class LastMessage { diff --git a/src/api/integrations/channel/whatsapp/read-state.ts b/src/api/integrations/channel/whatsapp/read-state.ts new file mode 100644 index 0000000000..a579f98501 --- /dev/null +++ b/src/api/integrations/channel/whatsapp/read-state.ts @@ -0,0 +1,74 @@ +import type { ReadMessageDto } from '@api/dto/chat.dto'; +import { isJidGroup, isLidUser, isPnUser, type proto, type WASocket } from 'baileys'; + +export class ReadStateValidationError extends Error {} + +type ReadStateClient = Pick; + +function isChatJid(jid: string): boolean { + return !!(isJidGroup(jid) || isPnUser(jid) || isLidUser(jid)); +} + +/** The legacy request still sends receipts alone. A lastMessage also syncs chat state to linked devices. */ +export async function syncMessageReadState(client: ReadStateClient, data: ReadMessageDto) { + if (!Array.isArray(data.readMessages)) { + throw new ReadStateValidationError('readMessages must be an array'); + } + + const lastMessage = data.lastMessage; + if (lastMessage) { + const { key, messageTimestamp } = lastMessage; + if ( + !key || + typeof key.remoteJid !== 'string' || + !isChatJid(key.remoteJid) || + typeof key.id !== 'string' || + !key.id.trim() || + typeof key.fromMe !== 'boolean' || + !Number.isSafeInteger(messageTimestamp) || + messageTimestamp <= 0 + ) { + throw new ReadStateValidationError('lastMessage requires a valid chat key and epoch-seconds messageTimestamp'); + } + if ( + data.readMessages.some( + (read) => + !read || + !isChatJid(read.remoteJid) || + read.remoteJid !== key.remoteJid || + !read.id || + typeof read.fromMe !== 'boolean', + ) + ) { + throw new ReadStateValidationError('readMessages and lastMessage must belong to the same chat'); + } + } else if (data.readMessages.length === 0) { + throw new ReadStateValidationError('readMessages cannot be empty without lastMessage'); + } + + const keys: proto.IMessageKey[] = data.readMessages + .filter((read) => isChatJid(read.remoteJid)) + .map((read) => ({ remoteJid: read.remoteJid, fromMe: read.fromMe, id: read.id })); + + // Run validation before either external call. A failure in either call propagates to the HTTP request. + if (lastMessage) { + await client.chatModify({ markRead: true, lastMessages: [lastMessage] }, lastMessage.key.remoteJid); + } + if (keys.length > 0 || !lastMessage) { + await client.readMessages(keys); + } + return { message: 'Read messages', read: 'success' }; +} + +export function chatReadStateUpdates( + chats: Array>, + instanceId: string, + readStateObservedAt: string, +) { + return chats.map((chat) => ({ + remoteJid: chat.id, + instanceId, + readStateObservedAt, + ...(Number.isInteger(chat.unreadCount) ? { unreadCount: chat.unreadCount } : {}), + })); +} diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 010e5deb8d..796536aed5 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -157,6 +157,7 @@ import { PassThrough, Readable } from 'stream'; import { v4 } from 'uuid'; import { BaileysMessageProcessor } from './baileysMessage.processor'; +import { chatReadStateUpdates, ReadStateValidationError, syncMessageReadState } from './read-state'; import { useVoiceCallsBaileys } from './voiceCalls/useVoiceCallsBaileys'; export interface ExtendedIMessageKey extends proto.IMessageKey { @@ -805,9 +806,8 @@ export class BaileysStartupService extends ChannelStartupService { } >[], ) => { - const chatsRaw = chats.map((chat) => { - return { remoteJid: chat.id, instanceId: this.instanceId }; - }); + const readStateObservedAt = new Date().toISOString(); + const chatsRaw = chatReadStateUpdates(chats, this.instanceId, readStateObservedAt); this.sendDataWebhook(Events.CHATS_UPDATE, chatsRaw); @@ -3699,15 +3699,11 @@ export class BaileysStartupService extends ChannelStartupService { public async markMessageAsRead(data: ReadMessageDto) { try { - const keys: proto.IMessageKey[] = []; - data.readMessages.forEach((read) => { - if (isJidGroup(read.remoteJid) || isPnUser(read.remoteJid)) { - keys.push({ remoteJid: read.remoteJid, fromMe: read.fromMe, id: read.id }); - } - }); - await this.client.readMessages(keys); - return { message: 'Read messages', read: 'success' }; + return await syncMessageReadState(this.client, data); } catch (error) { + if (error instanceof ReadStateValidationError) { + throw new BadRequestException(error.message); + } throw new InternalServerErrorException('Read messages fail', error.toString()); } } @@ -3773,7 +3769,7 @@ export class BaileysStartupService extends ChannelStartupService { last_message = await this.getLastMessage(number); } else { last_message = data.lastMessage; - last_message.messageTimestamp = last_message?.messageTimestamp ?? Date.now(); + last_message.messageTimestamp = last_message?.messageTimestamp ?? Math.floor(Date.now() / 1000); number = last_message?.key?.remoteJid; } @@ -4325,7 +4321,10 @@ export class BaileysStartupService extends ChannelStartupService { return null; } }); - __groupMetaChain = task.then(() => undefined, () => undefined); + __groupMetaChain = task.then( + () => undefined, + () => undefined, + ); return task; } diff --git a/src/validate/chat.schema.ts b/src/validate/chat.schema.ts index 7dae44539b..fc219bdd63 100644 --- a/src/validate/chat.schema.ts +++ b/src/validate/chat.schema.ts @@ -47,7 +47,6 @@ export const readMessageSchema: JSONSchema7 = { properties: { readMessages: { type: 'array', - minItems: 1, uniqueItems: true, items: { properties: { @@ -59,8 +58,25 @@ export const readMessageSchema: JSONSchema7 = { ...isNotEmpty('id', 'remoteJid'), }, }, + lastMessage: { + type: 'object', + properties: { + key: { + type: 'object', + properties: { + id: { type: 'string', minLength: 1 }, + fromMe: { type: 'boolean' }, + remoteJid: { type: 'string', minLength: 1 }, + }, + required: ['id', 'fromMe', 'remoteJid'], + }, + messageTimestamp: { type: 'integer', minimum: 1 }, + }, + required: ['key', 'messageTimestamp'], + }, }, required: ['readMessages'], + anyOf: [{ properties: { readMessages: { minItems: 1 } } }, { required: ['lastMessage'] }], }; export const archiveChatSchema: JSONSchema7 = { @@ -109,7 +125,7 @@ export const markChatUnreadSchema: JSONSchema7 = { required: ['id', 'fromMe', 'remoteJid'], ...isNotEmpty('id', 'remoteJid'), }, - messageTimestamp: { type: 'integer', minLength: 1 }, + messageTimestamp: { type: 'integer', minimum: 1 }, }, required: ['key'], ...isNotEmpty('messageTimestamp'), diff --git a/test/read-state.test.ts b/test/read-state.test.ts new file mode 100644 index 0000000000..cf6c91ca70 --- /dev/null +++ b/test/read-state.test.ts @@ -0,0 +1,117 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import type { ReadMessageDto } from '../src/api/dto/chat.dto'; +import { + chatReadStateUpdates, + ReadStateValidationError, + syncMessageReadState, +} from '../src/api/integrations/channel/whatsapp/read-state'; +import { readMessageSchema } from '../src/validate/chat.schema'; +import { Validator } from 'jsonschema'; + +const jid = '5511999999999@s.whatsapp.net'; +const otherJid = '5511888888888@s.whatsapp.net'; +const lastMessage = { key: { remoteJid: jid, fromMe: true, id: 'outbound-1' }, messageTimestamp: 1_790_000_000 }; +const receipt = { remoteJid: jid, fromMe: false, id: 'inbound-1' }; + +function mockClient(options: { failModify?: boolean; failReceipts?: boolean } = {}) { + const calls: Array<{ command: string; payload: unknown; jid?: string }> = []; + const client = { + async chatModify(payload: unknown, chatJid: string) { + calls.push({ command: 'chatModify', payload, jid: chatJid }); + if (options.failModify) throw new Error('modify failed'); + }, + async readMessages(keys: unknown) { + calls.push({ command: 'readMessages', payload: keys }); + if (options.failReceipts) throw new Error('receipts failed'); + }, + } as Parameters[0]; + return { client, calls }; +} + +test('legacy readMessages sends receipts without modifying chat state', async () => { + const { client, calls } = mockClient(); + const result = await syncMessageReadState(client, { readMessages: [receipt] }); + assert.deepEqual(result, { message: 'Read messages', read: 'success' }); + assert.deepEqual(calls, [{ command: 'readMessages', payload: [receipt] }]); +}); + +test('lastMessage synchronizes linked-device read state and sends inbound receipts', async () => { + const { client, calls } = mockClient(); + await syncMessageReadState(client, { readMessages: [receipt], lastMessage }); + assert.deepEqual(calls, [ + { command: 'chatModify', payload: { markRead: true, lastMessages: [lastMessage] }, jid }, + { command: 'readMessages', payload: [receipt] }, + ]); +}); + +test('outbound lastMessage can clear a manually unread chat without inbound receipts', async () => { + const { client, calls } = mockClient(); + await syncMessageReadState(client, { readMessages: [], lastMessage }); + assert.deepEqual(calls, [{ command: 'chatModify', payload: { markRead: true, lastMessages: [lastMessage] }, jid }]); +}); + +test('mixed chats and missing timestamps fail before SDK effects', async () => { + const { client, calls } = mockClient(); + await assert.rejects( + syncMessageReadState(client, { readMessages: [{ ...receipt, remoteJid: otherJid }], lastMessage }), + ReadStateValidationError, + ); + await assert.rejects( + syncMessageReadState(client, { readMessages: [receipt], lastMessage: { key: lastMessage.key } } as ReadMessageDto), + ReadStateValidationError, + ); + assert.deepEqual(calls, []); +}); + +test('request schema requires an epoch-seconds timestamp and allows empty receipts only with lastMessage', () => { + const validator = new Validator(); + const valid = (value: unknown) => validator.validate(value, readMessageSchema).valid; + assert.equal(valid({ readMessages: [receipt] }), true); + assert.equal(valid({ readMessages: [], lastMessage }), true); + assert.equal(valid({ readMessages: [] }), false); + assert.equal(valid({ readMessages: [receipt], lastMessage: { key: lastMessage.key } }), false); + assert.equal(valid({ readMessages: [receipt], lastMessage: { ...lastMessage, messageTimestamp: 0 } }), false); +}); + +test('LID chats are accepted when the installed Baileys SDK recognizes them', async () => { + const lid = '123456789@lid'; + const { client, calls } = mockClient(); + await syncMessageReadState(client, { + readMessages: [{ remoteJid: lid, fromMe: false, id: 'lid-inbound' }], + lastMessage: { key: { remoteJid: lid, fromMe: false, id: 'lid-inbound' }, messageTimestamp: 1_790_000_001 }, + }); + assert.equal(calls[0].command, 'chatModify'); + assert.equal(calls[0].jid, lid); + assert.equal(calls[1].command, 'readMessages'); +}); + +test('partial SDK failure never reports success', async () => { + const modify = mockClient({ failModify: true }); + await assert.rejects(syncMessageReadState(modify.client, { readMessages: [receipt], lastMessage }), /modify failed/); + assert.deepEqual(modify.calls.map((call) => call.command), ['chatModify']); + + const receipts = mockClient({ failReceipts: true }); + await assert.rejects(syncMessageReadState(receipts.client, { readMessages: [receipt], lastMessage }), /receipts failed/); + assert.deepEqual(receipts.calls.map((call) => call.command), ['chatModify', 'readMessages']); +}); + +test('chats.update retains zero, manual-unread marker, and positive delta with observation time', () => { + const observedAt = '2026-09-28T12:34:56.000Z'; + const updates = chatReadStateUpdates( + [ + { id: jid, unreadCount: 0 }, + { id: otherJid, unreadCount: -1 }, + { id: '123@g.us', unreadCount: 3 }, + { id: '456@g.us', unreadCount: 1.5 }, + ], + 'instance-1', + observedAt, + ); + assert.deepEqual(updates, [ + { remoteJid: jid, instanceId: 'instance-1', readStateObservedAt: observedAt, unreadCount: 0 }, + { remoteJid: otherJid, instanceId: 'instance-1', readStateObservedAt: observedAt, unreadCount: -1 }, + { remoteJid: '123@g.us', instanceId: 'instance-1', readStateObservedAt: observedAt, unreadCount: 3 }, + { remoteJid: '456@g.us', instanceId: 'instance-1', readStateObservedAt: observedAt }, + ]); +}); From b24f9d0d73ca72d7c1dd67aac6aa94c56d7d003c Mon Sep 17 00:00:00 2001 From: Alpes Digital Date: Mon, 28 Sep 2026 15:09:03 -0300 Subject: [PATCH 6/6] fix(test): isolate read-state tests from Baileys runtime --- src/api/integrations/channel/whatsapp/read-state.ts | 6 ++++-- test/read-state.test.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/read-state.ts b/src/api/integrations/channel/whatsapp/read-state.ts index a579f98501..d8eb89edee 100644 --- a/src/api/integrations/channel/whatsapp/read-state.ts +++ b/src/api/integrations/channel/whatsapp/read-state.ts @@ -1,12 +1,14 @@ import type { ReadMessageDto } from '@api/dto/chat.dto'; -import { isJidGroup, isLidUser, isPnUser, type proto, type WASocket } from 'baileys'; +import type { proto, WASocket } from 'baileys'; export class ReadStateValidationError extends Error {} type ReadStateClient = Pick; function isChatJid(jid: string): boolean { - return !!(isJidGroup(jid) || isPnUser(jid) || isLidUser(jid)); + // Baileys' isPnUser/isJidGroup/isLidUser check these suffixes. Keep this + // predicate local so focused tests need not load the full Baileys runtime. + return typeof jid === 'string' && /[^@\s]+@(s\.whatsapp\.net|g\.us|lid)$/.test(jid); } /** The legacy request still sends receipts alone. A lastMessage also syncs chat state to linked devices. */ diff --git a/test/read-state.test.ts b/test/read-state.test.ts index cf6c91ca70..771f72f94f 100644 --- a/test/read-state.test.ts +++ b/test/read-state.test.ts @@ -74,7 +74,7 @@ test('request schema requires an epoch-seconds timestamp and allows empty receip assert.equal(valid({ readMessages: [receipt], lastMessage: { ...lastMessage, messageTimestamp: 0 } }), false); }); -test('LID chats are accepted when the installed Baileys SDK recognizes them', async () => { +test('LID chats are accepted by the same JID suffix contract', async () => { const lid = '123456789@lid'; const { client, calls } = mockClient(); await syncMessageReadState(client, {