From 64e276dab938025f6e58eb88a4cd7ed780e55267 Mon Sep 17 00:00:00 2001 From: Alexandre Reyes Martins Date: Wed, 11 Feb 2026 21:21:16 +0000 Subject: [PATCH 1/3] fix(whatsapp): ensure audio messages always emit upsert events --- .../whatsapp/whatsapp.baileys.service.ts | 120 +++++++++++------- 1 file changed, 77 insertions(+), 43 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 60e857fcc..85baedcdc 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1389,50 +1389,49 @@ export class BaileysStartupService extends ChannelStartupService { try { if (isVideo && !this.configService.get('S3').SAVE_VIDEO) { this.logger.warn('Video upload is disabled. Skipping video upload.'); - // Skip video upload by returning early from this block - return; - } - - const message: any = received; - - // Verificação adicional para garantir que há conteúdo de mídia real - const hasRealMedia = this.hasValidMediaContent(message); - - if (!hasRealMedia) { - this.logger.warn('Message detected as media but contains no valid media content'); } else { - const media = await this.getBase64FromMediaMessage({ message }, true); - - if (!media) { - this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO'); - return; + const message: any = received; + + // Verificação adicional para garantir que há conteúdo de mídia real + const hasRealMedia = this.hasValidMediaContent(message); + + if (!hasRealMedia) { + this.logger.warn('Message detected as media but contains no valid media content'); + } else { + const media = await this.getBase64FromMediaMessage({ message }, true); + + if (!media) { + this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO'); + } else { + const { buffer, mediaType, fileName, size } = media; + const mimetype = mimeTypes.lookup(fileName).toString(); + const fullName = join( + `${this.instance.id}`, + received.key.remoteJid, + mediaType, + `${Date.now()}_${fileName}`, + ); + await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { + 'Content-Type': mimetype, + }); + + await this.prismaRepository.media.create({ + data: { + messageId: msg.id, + instanceId: this.instanceId, + type: mediaType, + fileName: fullName, + mimetype, + }, + }); + + const mediaUrl = await s3Service.getObjectUrl(fullName); + + messageRaw.message.mediaUrl = mediaUrl; + + await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw }); + } } - - const { buffer, mediaType, fileName, size } = media; - const mimetype = mimeTypes.lookup(fileName).toString(); - const fullName = join( - `${this.instance.id}`, - received.key.remoteJid, - mediaType, - `${Date.now()}_${fileName}`, - ); - await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { 'Content-Type': mimetype }); - - await this.prismaRepository.media.create({ - data: { - messageId: msg.id, - instanceId: this.instanceId, - type: mediaType, - fileName: fullName, - mimetype, - }, - }); - - const mediaUrl = await s3Service.getObjectUrl(fullName); - - messageRaw.message.mediaUrl = mediaUrl; - - await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw }); } } catch (error) { this.logger.error(['Error on upload file to minio', error?.message, error?.stack]); @@ -1480,7 +1479,11 @@ export class BaileysStartupService extends ChannelStartupService { } console.log(messageRaw); - this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + + if (messageRaw.messageType === 'audioMessage' && !messageRaw.key.fromMe && messageRaw.key.id) { + await this.baileysCache.set(`upsert_emitted_${this.instanceId}_${messageRaw.key.id}`, true, 60 * 10); + } await chatbotController.emit({ instance: { instanceName: this.instance.name, instanceId: this.instanceId }, @@ -1649,6 +1652,37 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.warn(`Original message not found for update. Skipping. Key: ${JSON.stringify(key)}`); continue; } + + if (!key.fromMe && findMessage.messageType === 'audioMessage' && key.id) { + const upsertCacheKey = `upsert_emitted_${this.instanceId}_${key.id}`; + const alreadyEmitted = await this.baileysCache.get(upsertCacheKey); + + if (!alreadyEmitted) { + const fallbackUpsertPayload = { + key: findMessage.key, + pushName: findMessage.pushName, + status: findMessage.status, + message: findMessage.message, + contextInfo: findMessage.contextInfo, + messageType: findMessage.messageType, + messageTimestamp: findMessage.messageTimestamp, + instanceId: findMessage.instanceId, + source: findMessage.source, + }; + + try { + await this.sendDataWebhook(Events.MESSAGES_UPSERT, fallbackUpsertPayload); + await this.baileysCache.set(upsertCacheKey, true, 60 * 10); + this.logger.warn(`Fallback messages.upsert emitted for audio message ${key.id}`); + } catch (error) { + this.logger.error([ + `Failed to emit fallback messages.upsert for audio message ${key.id}`, + error?.message, + ]); + } + } + } + message.messageId = findMessage.id; } From 24e6695e7765ed539adac62a0a8f0abeab6bcaf5 Mon Sep 17 00:00:00 2001 From: Alexandre Reyes Martins Date: Wed, 11 Feb 2026 21:21:41 +0000 Subject: [PATCH 2/3] ci: publish Docker image to GHCR on fork pushes --- .github/workflows/publish_ghcr_image.yml | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/publish_ghcr_image.yml diff --git a/.github/workflows/publish_ghcr_image.yml b/.github/workflows/publish_ghcr_image.yml new file mode 100644 index 000000000..5dd7fab7b --- /dev/null +++ b/.github/workflows/publish_ghcr_image.yml @@ -0,0 +1,66 @@ +name: Build and Publish GHCR image + +on: + push: + branches: + - main + tags: + - "v*" + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/evolution-api + +jobs: + build-and-push: + name: Build and Push GHCR + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + submodules: recursive + + - 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: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=branch + type=ref,event=tag + type=sha + + - name: Build and push + id: build-and-push + uses: docker/build-push-action@v6 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Image digest + run: echo ${{ steps.build-and-push.outputs.digest }} From 2fd3ee3b0436afa4b1d492ee0101a63f307ce74c Mon Sep 17 00:00:00 2001 From: Alexandre Reyes Martins Date: Thu, 12 Feb 2026 08:07:52 +0000 Subject: [PATCH 3/3] chore(whatsapp): remove raw upsert logging and centralize cache key --- .../channel/whatsapp/whatsapp.baileys.service.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 85baedcdc..42862420f 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -553,6 +553,10 @@ export class BaileysStartupService extends ChannelStartupService { } } + private getUpsertEmittedCacheKey(messageId: string) { + return `upsert_emitted_${this.instanceId}_${messageId}`; + } + private async defineAuthState() { const db = this.configService.get('DATABASE'); const cache = this.configService.get('CACHE'); @@ -1477,12 +1481,10 @@ export class BaileysStartupService extends ChannelStartupService { if (messageRaw.key.remoteJid?.includes('@lid') && messageRaw.key.remoteJidAlt) { messageRaw.key.remoteJid = messageRaw.key.remoteJidAlt; } - console.log(messageRaw); - await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); if (messageRaw.messageType === 'audioMessage' && !messageRaw.key.fromMe && messageRaw.key.id) { - await this.baileysCache.set(`upsert_emitted_${this.instanceId}_${messageRaw.key.id}`, true, 60 * 10); + await this.baileysCache.set(this.getUpsertEmittedCacheKey(messageRaw.key.id), true, 60 * 10); } await chatbotController.emit({ @@ -1654,7 +1656,7 @@ export class BaileysStartupService extends ChannelStartupService { } if (!key.fromMe && findMessage.messageType === 'audioMessage' && key.id) { - const upsertCacheKey = `upsert_emitted_${this.instanceId}_${key.id}`; + const upsertCacheKey = this.getUpsertEmittedCacheKey(key.id); const alreadyEmitted = await this.baileysCache.get(upsertCacheKey); if (!alreadyEmitted) {