Skip to content

Conversation

@VCalazans
Copy link
Contributor

@VCalazans VCalazans commented Sep 8, 2025

Summary by Sourcery

Improve WhatsApp Baileys integration with JID normalization, corrupted session clearing, enriched logging, automatic PENDING-to-SERVER_ACK transitions, and robust message status handling. Enhance OpenAI controller by defaulting speech-to-text, auto-assigning new credentials, and updating Prisma schema for the new default.

New Features:

  • Add normalizeJid and clearCorruptedSessionData utilities for WhatsApp integration
  • Schedule automatic status update for PENDING messages after timeout
  • Add setCredentialAsDefault method to auto-assign new OpenAI credentials

Bug Fixes:

  • Prevent duplicate message status updates and handle corrupted Baileys sessions by clearing stale cache entries
  • Ensure correct format and re-verification logic for Brazilian numbers in onWhatsApp cache

Enhancements:

  • Normalize JIDs across caching, chat lookup, message sending, and reactions to improve consistency
  • Log connection state changes, message send attempts, and errors throughout Baileys service
  • Wrap conversation message sending in try/catch with success/failure logging
  • Improve message.update handling to process read receipts and update statuses atomically

Build:

  • Add Prisma migrations to change OpenaiSetting.speechToText default to true and update existing records

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Sep 8, 2025

Reviewer's Guide

This PR merges changes from the official repo, introducing a normalizeJid utility, robust session clearing, enhanced logging, automated PENDING status reconciliation, refactored read/ status handling with caching, improved on-WhatsApp number verification, default speech-to-text toggles and credential management in the OpenAI controller, and adds database migrations to align defaults.

Sequence diagram for automatic PENDING message status reconciliation

sequenceDiagram
  participant BaileysStartupService
  participant PrismaRepository
  participant Logger
  participant Webhook

  BaileysStartupService->>Logger: Log PENDING message sent
  BaileysStartupService->>BaileysStartupService: setTimeout(30s)
  BaileysStartupService->>PrismaRepository: findFirst(message with status PENDING)
  alt still pending
    BaileysStartupService->>Logger: Warn forcing status update
    BaileysStartupService->>PrismaRepository: update(message.status = SERVER_ACK)
    BaileysStartupService->>Webhook: sendDataWebhook(MESSAGES_UPDATE)
  else not pending
    BaileysStartupService->>Logger: No action
  end
Loading

Sequence diagram for OpenaiController credential creation and default setting

sequenceDiagram
  participant User as actor
  participant OpenaiController
  participant CredsRepository
  participant SettingsRepository
  participant Logger

  User->>OpenaiController: createCreds(data)
  OpenaiController->>CredsRepository: findFirst(name exists?)
  alt name exists
    OpenaiController->>Logger: error
    OpenaiController-->>User: throw error
  else name does not exist
    OpenaiController->>CredsRepository: create(data)
    OpenaiController->>SettingsRepository: findFirst(instanceId)
    alt settings exist
      OpenaiController->>SettingsRepository: update(OpenaiCreds connect)
    else settings do not exist
      OpenaiController->>SettingsRepository: create(OpenaiCreds connect)
    end
    OpenaiController-->>User: return creds
  end
Loading

Flow diagram for onWhatsappCache number normalization and prioritization

flowchart TD
  A["Input remoteJid"] --> B["Check if Brazilian number"]
  B -- Yes --> C["Prioritize format with 9"]
  C --> D["Add with 9 @domain"]
  C --> E["Add without 9 @domain"]
  B -- No --> F["Check if Mexican/Argentinian number"]
  F -- Yes --> G["Handle prefix and digit"]
  G --> H["Add with digit @domain"]
  G --> I["Add without digit @domain"]
  F -- No --> J["Other countries"]
  J --> K["Add remoteJid as is"]
  D & E & H & I & K --> L["Return numbersAvailable"]
Loading

File-Level Changes

Change Details Files
JID normalization across message flows
  • Added normalizeJid helper to standardize LID/JID and group JIDs
  • Applied normalization to message keys, database queries, and participant fields
  • Updated raw message construction to use normalized JIDs
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Session data clearing on startup
  • Introduced clearCorruptedSessionData to purge bailies cache patterns
  • Invoked session clearing before socket connection
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Enhanced logging throughout socket lifecycle
  • Logged connection state changes and disconnect errors
  • Wrapped sendMessage with success/failure logs
  • Warned when messages remain in PENDING status
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Automatic update for PENDING messages
  • Scheduled 30s timeout to force SERVER_ACK on still-pending messages
  • Emitted webhook for status updates after timeout
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Refactored read receipts and status updates
  • Unified caching key format with normalized JID
  • Checked and updated read status with caching guard
  • Centralized message status updates via Prisma and logged transitions
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Improved on-WhatsApp number verification
  • Filtered verification list to re-verify Brazilian numbers when both formats exist
  • Skipped onWhatsApp call when no numbers to verify
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
src/utils/onWhatsappCache.ts
OpenAI controller defaults and credential management
  • Enabled speechToText default to true and adjusted migrations
  • Commented out duplicate API key check
  • Added setCredentialAsDefault to auto-assign new creds in settings
src/api/integrations/chatbot/openai/controllers/openai.controller.ts
prisma/mysql-migrations/20250709000000_change_speech_to_text_default_to_true/migration.sql
prisma/postgresql-migrations/20250709000000_change_speech_to_text_default_to_true/migration.sql
onWhatsappCache format prioritization
  • Prioritized adding JIDs with '9' first for Brazilian numbers
  • Returned full JID entries instead of raw numbers
src/utils/onWhatsappCache.ts

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:

  • Instead of hardcoding the 30-second timeout for forcing PENDING messages to SERVER_ACK, extract it into a configurable constant or environment variable so it can be tuned without code changes.
  • clearCorruptedSessionData currently deletes all cache entries with broad patterns on every startup—consider narrowing its scope to only truly corrupted keys to avoid unintended data loss and heavy cache operations.
  • The normalizeJid implementation relies on simple string splits and includes checks—consider centralizing JID parsing into a shared utility or library and handling edge cases like whitespace or unexpected suffixes for greater robustness.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Instead of hardcoding the 30-second timeout for forcing PENDING messages to SERVER_ACK, extract it into a configurable constant or environment variable so it can be tuned without code changes.
- clearCorruptedSessionData currently deletes all cache entries with broad patterns on every startup—consider narrowing its scope to only truly corrupted keys to avoid unintended data loss and heavy cache operations.
- The normalizeJid implementation relies on simple string splits and includes checks—consider centralizing JID parsing into a shared utility or library and handling edge cases like whitespace or unexpected suffixes for greater robustness.

## Individual Comments

### Comment 1
<location> `src/utils/onWhatsappCache.ts:20` </location>
<code_context>
       number.slice(4, 5) === '9' && number.length === 13 ? number : `${number.slice(0, 4)}9${number.slice(4)}`;
     const numberWithoutDigit = number.length === 12 ? number : number.slice(0, 4) + number.slice(5);

-    numbersAvailable.push(numberWithDigit);
-    numbersAvailable.push(numberWithoutDigit);
+    // Add the format WITH 9 first (prioritized)
+    numbersAvailable.push(`${numberWithDigit}@${domain || 's.whatsapp.net'}`);
+    // Add the format WITHOUT 9 second (fallback)
+    numbersAvailable.push(`${numberWithoutDigit}@${domain || 's.whatsapp.net'}`);
</code_context>

<issue_to_address>
Avoid duplicate domain appending in getAvailableNumbers.

Check that numbers passed to this function do not already include a domain to prevent appending it twice.

Suggested implementation:

```typescript
    // Add the format WITH 9 first (prioritized)
    if (!numberWithDigit.includes('@')) {
      numbersAvailable.push(`${numberWithDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithDigit);
    }
    // Add the format WITHOUT 9 second (fallback)
    if (!numberWithoutDigit.includes('@')) {
      numbersAvailable.push(`${numberWithoutDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithoutDigit);
    }

```

```typescript
    if (!numberWithDigit.includes('@')) {
      numbersAvailable.push(`${numberWithDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithDigit);
    }
    if (!numberWithoutDigit.includes('@')) {
      numbersAvailable.push(`${numberWithoutDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithoutDigit);
    }

```
</issue_to_address>

### Comment 2
<location> `src/api/integrations/chatbot/openai/controllers/openai.controller.ts:239` </location>
<code_context>
     if (!data.name) throw new BadRequestException('Name is required');

     // Check if API key already exists
-    const existingApiKey = await this.credsRepository.findFirst({
-      where: {
-        apiKey: data.apiKey,
-      },
-    });
+    // const existingApiKey = await this.credsRepository.findFirst({
+    //   where: {
+    //     apiKey: data.apiKey,
+    //   },
+    // });

-    if (existingApiKey) {
-      throw new BadRequestException('This API key is already registered. Please use a different API key.');
-    }
</code_context>

<issue_to_address>
Reconsider removal of duplicate API key check.

If allowing duplicate API keys, confirm that downstream processes are robust against this and that no security or operational issues will arise.
</issue_to_address>

### Comment 3
<location> `prisma/mysql-migrations/20250709000000_change_speech_to_text_default_to_true/migration.sql:5` </location>
<code_context>
+ALTER TABLE `OpenaiSetting` MODIFY COLUMN `speechToText` BOOLEAN NULL DEFAULT true;
+
+-- Update existing records to use the new default
+UPDATE `OpenaiSetting` SET `speechToText` = true WHERE `speechToText` IS NULL OR `speechToText` = false;
</code_context>

<issue_to_address>
Migration may overwrite intentional false values.

Currently, the migration updates both NULL and false values to true. If false values are intentional, you may want to update only NULL values instead.
</issue_to_address>

### Comment 4
<location> `prisma/postgresql-migrations/20250709000000_change_speech_to_text_default_to_true/migration.sql:5` </location>
<code_context>
+ALTER TABLE "OpenaiSetting" ALTER COLUMN "speechToText" SET DEFAULT true;
+
+-- Update existing records to use the new default
+UPDATE "OpenaiSetting" SET "speechToText" = true WHERE "speechToText" IS NULL OR "speechToText" = false;
</code_context>

<issue_to_address>
Migration may overwrite intentional false values.

This update will change all false or NULL values to true, potentially overriding intentional false settings. Please confirm if this aligns with the desired migration behavior.
</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.

Comment on lines -20 to -21
numbersAvailable.push(numberWithDigit);
numbersAvailable.push(numberWithoutDigit);
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Avoid duplicate domain appending in getAvailableNumbers.

Check that numbers passed to this function do not already include a domain to prevent appending it twice.

Suggested implementation:

    // Add the format WITH 9 first (prioritized)
    if (!numberWithDigit.includes('@')) {
      numbersAvailable.push(`${numberWithDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithDigit);
    }
    // Add the format WITHOUT 9 second (fallback)
    if (!numberWithoutDigit.includes('@')) {
      numbersAvailable.push(`${numberWithoutDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithoutDigit);
    }
    if (!numberWithDigit.includes('@')) {
      numbersAvailable.push(`${numberWithDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithDigit);
    }
    if (!numberWithoutDigit.includes('@')) {
      numbersAvailable.push(`${numberWithoutDigit}@${domain || 's.whatsapp.net'}`);
    } else {
      numbersAvailable.push(numberWithoutDigit);
    }

Comment on lines -239 to -245
const existingApiKey = await this.credsRepository.findFirst({
where: {
apiKey: data.apiKey,
},
});
// const existingApiKey = await this.credsRepository.findFirst({
// where: {
// apiKey: data.apiKey,
// },
// });

if (existingApiKey) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🚨 question (security): Reconsider removal of duplicate API key check.

If allowing duplicate API keys, confirm that downstream processes are robust against this and that no security or operational issues will arise.

ALTER TABLE `OpenaiSetting` MODIFY COLUMN `speechToText` BOOLEAN NULL DEFAULT true;

-- Update existing records to use the new default
UPDATE `OpenaiSetting` SET `speechToText` = true WHERE `speechToText` IS NULL OR `speechToText` = false;
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): Migration may overwrite intentional false values.

Currently, the migration updates both NULL and false values to true. If false values are intentional, you may want to update only NULL values instead.

ALTER TABLE "OpenaiSetting" ALTER COLUMN "speechToText" SET DEFAULT true;

-- Update existing records to use the new default
UPDATE "OpenaiSetting" SET "speechToText" = true WHERE "speechToText" IS NULL OR "speechToText" = false;
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): Migration may overwrite intentional false values.

This update will change all false or NULL values to true, potentially overriding intentional false settings. Please confirm if this aligns with the desired migration behavior.

@DavidsonGomes DavidsonGomes changed the base branch from main to develop September 8, 2025 23:43
@VCalazans VCalazans closed this Sep 8, 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.

2 participants