Skip to content

Conversation

@grichaud
Copy link

@grichaud grichaud commented Oct 25, 2025

πŸ“‹ Description

πŸ”— Related Issue

Closes #(issue_number)

πŸ§ͺ Type of Change

  • πŸ› Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • πŸ’₯ Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • πŸ“š Documentation update
  • πŸ”§ Refactoring (no functional changes)
  • ⚑ Performance improvement
  • 🧹 Code cleanup
  • πŸ”’ Security fix

πŸ§ͺ Testing

  • Manual testing completed
  • Functionality verified in development environment
  • No breaking changes introduced
  • Tested with different connection types (if applicable)

πŸ“Έ Screenshots (if applicable)

βœ… Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have manually tested my changes thoroughly
  • I have verified the changes work with different scenarios
  • Any dependent changes have been merged and published

πŸ“ Additional Notes

Summary by Sourcery

Add HTTP auth header support for Chatwoot and streamline WhatsApp integration by introducing lid mode handling, simplifying media attachment workflows, improving onWhatsApp cache logic, refining logging, and enhancing CI/build configurations.

New Features:

  • Use 'api-access-token' HTTP header for Chatwoot API calls
  • Add LID addressing mode and group JID detection in conversation and contact workflows

Bug Fixes:

  • Fix serialization of binary message buffers to Uint8Array in Baileys service
  • Prevent duplicate message processing by refining cache and requestId logic

Enhancements:

  • Remove complex media download and retry logic in Chatwoot and use direct URLs with randomized send delays
  • Refactor onWhatsappCache to merge existing JIDs and support lid and g.us domains
  • Simplify webhook deletion flow and standardize initial delay in Chatwoot service
  • Standardize logging levels and verbosity across services

Build:

  • Bump package version to 2.3.6, update Baileys dependency to v7.0.0-rc.6, and refresh Docker image tags

CI:

  • Enable 'submodules: recursive' in checkout steps for CI workflows
  • Add a new GitHub Actions workflow for building and pushing Docker images

Documentation:

  • Update CHANGELOG for version 2.3.6 and adjust issue template default version

DavidsonGomes and others added 30 commits October 15, 2025 09:25
- Updated subproject reference in evolution-manager-v2.
- Replaced old JavaScript and CSS asset files with new versions for improved performance and styling.
- Added new CSS file for consistent font styling across the application.
- Updated the evolution logo image to the latest version.
- Added features for Chatwoot enhancements, participants data handling, and LID to phone number conversion.
- Updated Docker configurations to include Kafka and frontend services.
- Fixed PostgreSQL migration errors and improved message handling in Baileys and Chatwoot services.
- Refactored TypeScript build process and implemented exponential backoff patterns.
- Updated the release date for version 2.3.5 to 2025-10-15.
- Adjusted subproject reference in evolution-manager-v2 to the latest commit.
- Integrated telemetry logging for received messages in Evolution, WhatsApp Business, and Baileys services.
- Enhanced message tracking by sending the message type to the telemetry system for better observability.
- Updated subproject reference in evolution-manager-v2 to the latest commit.
- Enhanced the manager_install.sh script to include npm install and build steps for the evolution-manager-v2.
- Replaced old JavaScript asset file with a new version for improved performance.
- Added a new CSS file for consistent styling across the application.
- Added 'submodules: recursive' option to the checkout step in multiple workflow files to ensure submodules are properly initialized during CI/CD processes.
…na baileys e chatwoot

* corrige cache de nΓΊmeros PN, LIDs e g.us para enviar o nΓΊmero correto
* atualiza para os ΓΊltimos commits da baileys
* corrige envio de Γ‘udio e documentos via chatwoot no canal baileys
* diversas correΓ§Γ΅es na integraΓ§Γ£o com chatwoot
* corrige mensagens ignoradas no recebimento de leads
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
…e.ts

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
…e.ts

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
feat(baileys,chatwoot,on-whatsapp-cache): implementaΓ§Γ΅es e correΓ§Γ΅es na baileys e chatwoot
Evita o erro de this.isZero not is function
fix: Simplify logging of messageSent object
fix( baileys.service ): Corrige ao salvar no DB valores Uint8Array
chore: bump version to 2.3.6 and update baileys dependency to 7.0.0-rc.6
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 25, 2025

Reviewer's Guide

This PR introduces Chatwoot auth header support while comprehensively refactoring message and attachment handling, contact resolution, cache management, logging, and CI workflows to simplify code paths and improve reliability.

Sequence diagram for Chatwoot message/attachment sending with auth header

sequenceDiagram
  participant S as "ChatwootService"
  participant C as "Chatwoot API"
  participant W as "WhatsApp Channel"
  S->>C: POST /conversations/:id/messages
  Note right of C: Now uses 'api-access-token' header
  C-->>S: Message/attachment accepted
  S->>W: Send message/attachment to WhatsApp
  W-->>S: Delivery status
Loading

Class diagram for OnWhatsappCache and related cache logic

classDiagram
  class OnWhatsappCache {
    +saveOnWhatsappCache(data)
    +getAvailableNumbers(remoteJid)
  }
  class ISaveOnWhatsappCacheParams {
    +remoteJid: string
    +remoteJidAlt: string
    +lid: "lid" | undefined
  }
  OnWhatsappCache -- ISaveOnWhatsappCacheParams
  OnWhatsappCache : Logger logger
  OnWhatsappCache : prismaRepository
  OnWhatsappCache : configService
  OnWhatsappCache : dayjs
  OnWhatsappCache : getAvailableNumbers(remoteJid)
  OnWhatsappCache : saveOnWhatsappCache(data)
  OnWhatsappCache : Handles @lid, @g.us, and normal numbers
  OnWhatsappCache : Preserves and merges jidOptions
Loading

Class diagram for ChatwootService message/attachment handling

classDiagram
  class ChatwootService {
    +createConversation(instance, body)
    +sendAttachment(waInstance, number, media, caption, options)
    +receiveWebhook(instance, body)
    +updateChatwootMessageId(message, chatwootMessageIds, instance)
    -logger
    -cache
    -waMonitor
    -prismaRepository
    -provider
  }
  ChatwootService : Uses 'api-access-token' header for Chatwoot API
  ChatwootService : Handles phone number resolution for LID and group
  ChatwootService : Simplified attachment download and sending
  ChatwootService : Refactored webhook and deletion logic
Loading

File-Level Changes

Change Details Files
Chatwoot API auth header updated
  • Replaced 'api_access_token' header with 'api-access-token'
  • Adjusted headers in both message POST configurations
src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts
Simplified attachment download and sending
  • Removed custom S3/HTTP retry and download backoff logic
  • Parsed media path and MIME type via path.parse and mimeTypes.lookup
  • Replaced base64 conversions with direct media URL and randomized delay
src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts
Refactored conversation creation and contact updates
  • Unified LID and group number resolution into a single phoneNumber variable
  • Streamlined identifier update and contact merge logic
  • Adjusted participant handling to use remoteJid and remoteJidAlt consistently
src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts
Removed obsolete retry/delay constants and background processing
  • Deleted MIN_CONNECTION_NOTIFICATION_INTERVAL_MS and WEBHOOK_INITIAL_DELAY_MS
  • Removed S3, database polling, and media download timeout constants
  • Eliminated setImmediate background deletion and attachment handlers
src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts
Streamlined updateChatwootMessageId logic
  • Dropped exponential backoff polling for DB message existence
  • Applied immediate SQL update using instance.instanceId
  • Removed verbose skip logs and instance fallback logic
src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts
Enhanced onWhatsapp cache management
  • Added support for remoteJidAlt and 'lid' domain in cache entries
  • Merged existing jidOptions instead of overwriting
  • Adjusted getAvailableNumbers to handle g.us and lid domains
src/utils/onWhatsappCache.ts
Improved logging and deduplication in Baileys services
  • Filtered duplicates by requestId in message upsert
  • Replaced logger.log calls with verbose/debug for consistency
  • Propagated contextInfo through sendMessage and updated message processor types
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
src/api/integrations/channel/whatsapp/baileysMessage.processor.ts
Release version bump and CI workflow updates
  • Bumped package version to 2.3.6 and updated Baileys to rc.6
  • Extended CHANGELOG with new features and fixes
  • Added submodules: recursive to multiple GitHub workflows and introduced docker-build workflow
package.json
CHANGELOG.md
.github/workflows/security.yml
.github/workflows/check_code_quality.yml
.github/workflows/publish_docker_image.yml
.github/workflows/publish_docker_image_homolog.yml
.github/workflows/publish_docker_image_latest.yml
.github/workflows/docker-build.yml
Docker/swarm/evolution_api_v2.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

Blocking issues:

  • An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. (link)
  • An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. (link)
  • An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. (link)

General comments:

  • There’s a lot of repeated JID parsing and LID/Group detection logic scattered across methodsβ€”consider extracting that into a shared helper to reduce duplication and improve readability.
  • Random delay values and hard-coded timeouts are sprinkled throughout (e.g. 500ms, 1200ms, random between 500–2000ms); centralizing these into named constants or config entries would make behavior easier to tune and reason about.
  • The sequential upserts in saveOnWhatsappCache use awaits inside a loop, which may hurt performance; consider batching those writes or wrapping them in a single transaction to handle multiple records more efficiently.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There’s a lot of repeated JID parsing and LID/Group detection logic scattered across methodsβ€”consider extracting that into a shared helper to reduce duplication and improve readability.
- Random delay values and hard-coded timeouts are sprinkled throughout (e.g. 500ms, 1200ms, random between 500–2000ms); centralizing these into named constants or config entries would make behavior easier to tune and reason about.
- The sequential upserts in saveOnWhatsappCache use awaits inside a loop, which may hurt performance; consider batching those writes or wrapping them in a single transaction to handle multiple records more efficiently.

## Individual Comments

### Comment 1
<location> `src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts:1157` </location>
<code_context>
-        this.logger.error(`[MEDIA DOWNLOAD] Error stack: ${downloadError.stack}`);
-        this.logger.error(`[MEDIA DOWNLOAD] Full error: ${JSON.stringify(downloadError, null, 2)}`);
-        throw new Error(`Failed to download media: ${downloadError.message}`);
+        mimeType = response.headers['content-type'];
       }

</code_context>

<issue_to_address>
**suggestion:** Mime type assignment may be undefined if response header is missing.

If 'content-type' is missing, mimeType will be undefined. Please handle this case by setting a default value or adding a check.

```suggestion
        mimeType = response.headers['content-type'] || 'application/octet-stream';
```
</issue_to_address>

### Comment 2
<location> `src/utils/onWhatsappCache.ts:121-128` </location>
<code_context>
+        `Saving: remoteJid=${remoteJid}, jidOptions=${uniqueNumbers.join(',')}, lid=${item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null}`,
+      );
+
+      if (existingRecord) {
+        await prismaRepository.isOnWhatsapp.update({
+          where: { id: existingRecord.id },
+          data: {
+            remoteJid: remoteJid,
+            jidOptions: uniqueNumbers.join(','),
+            lid: item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null,
+          },
+        });
+      } else {
+        await prismaRepository.isOnWhatsapp.create({
+          data: {
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Upsert logic may overwrite existing lid value.

Preserve the existing lid value during updates if it is already set and valid, rather than always overwriting it based on the current item.

```suggestion
        // Determine if we should preserve the existing lid value
        const newLid = item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null;
        const existingLidValid = existingRecord.lid === 'lid';

        await prismaRepository.isOnWhatsapp.update({
          where: { id: existingRecord.id },
          data: {
            remoteJid: remoteJid,
            jidOptions: uniqueNumbers.join(','),
            lid: existingLidValid && !newLid ? existingRecord.lid : newLid,
          },
        });
```
</issue_to_address>

### Comment 3
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:4452-4460` </location>
<code_context>
   }

-  private convertLongToNumber(obj: any): any {
+  private deserializeMessageBuffers(obj: any): any {
     if (obj === null || obj === undefined) {
       return obj;
     }

-    if (Long.isLong(obj)) {
-      return obj.toNumber();
+    if (typeof obj === 'object' && !Array.isArray(obj) && !Buffer.isBuffer(obj)) {
+      const keys = Object.keys(obj);
+      const isIndexedObject = keys.every((key) => !isNaN(Number(key)));
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Deserialization may incorrectly convert objects with numeric keys.

Stricter validation is needed to ensure only intended buffer objects are converted, preventing misclassification of other objects with numeric keys.

```suggestion
    if (typeof obj === 'object' && !Array.isArray(obj) && !Buffer.isBuffer(obj)) {
      const keys = Object.keys(obj);
      // Stricter validation: keys must be consecutive numeric strings starting from 0
      const isIndexedObject =
        keys.length > 0 &&
        keys.every((key, idx) => key === String(idx)) &&
        keys.every((key) => {
          const value = obj[key];
          return typeof value === 'number' && value >= 0 && value <= 255;
        });

      if (isIndexedObject) {
        const values = keys.map((key) => obj[key]);
        return new Uint8Array(values);
      }
    }
```
</issue_to_address>

### Comment 4
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:4498-4499` </location>
<code_context>
       status: status[message.status],
-      message: this.convertLongToNumber({ ...message.message }),
-      contextInfo: this.convertLongToNumber(contentMsg?.contextInfo),
+      message: this.deserializeMessageBuffers({ ...message.message }),
+      contextInfo: this.deserializeMessageBuffers(contentMsg?.contextInfo),
       messageType: contentType || 'unknown',
       messageTimestamp: Long.isLong(message.messageTimestamp)
</code_context>

<issue_to_address>
**issue (bug_risk):** Deserialization of message and contextInfo may lose Long type information.

Verify that deserialization preserves Long values for fields like timestamps and IDs, and apply necessary conversions if not.
</issue_to_address>

### Comment 5
<location> `.github/workflows/docker-build.yml:18` </location>
<code_context>
        uses: docker/setup-buildx-action@v3
</code_context>

<issue_to_address>
**security (yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha):** An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.

*Source: opengrep*
</issue_to_address>

### Comment 6
<location> `.github/workflows/docker-build.yml:21` </location>
<code_context>
        uses: docker/login-action@v3
</code_context>

<issue_to_address>
**security (yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha):** An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.

*Source: opengrep*
</issue_to_address>

### Comment 7
<location> `.github/workflows/docker-build.yml:27` </location>
<code_context>
        uses: docker/build-push-action@v5
</code_context>

<issue_to_address>
**security (yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha):** An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click πŸ‘ or πŸ‘Ž on each comment and I'll use the feedback to improve your reviews.

this.logger.error(`[MEDIA DOWNLOAD] Error stack: ${downloadError.stack}`);
this.logger.error(`[MEDIA DOWNLOAD] Full error: ${JSON.stringify(downloadError, null, 2)}`);
throw new Error(`Failed to download media: ${downloadError.message}`);
mimeType = response.headers['content-type'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Mime type assignment may be undefined if response header is missing.

If 'content-type' is missing, mimeType will be undefined. Please handle this case by setting a default value or adding a check.

Suggested change
mimeType = response.headers['content-type'];
mimeType = response.headers['content-type'] || 'application/octet-stream';

Comment on lines +4498 to +4499
message: this.deserializeMessageBuffers({ ...message.message }),
contextInfo: this.deserializeMessageBuffers(contentMsg?.contextInfo),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Deserialization of message and contextInfo may lose Long type information.

Verify that deserialization preserves Long values for fields like timestamps and IDs, and apply necessary conversions if not.

@grichaud grichaud closed this Oct 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants