Skip to content

Log webhook #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 81 commits into
base: main
Choose a base branch
from
Open

Log webhook #7

wants to merge 81 commits into from

Conversation

jhonsw1
Copy link

@jhonsw1 jhonsw1 commented May 13, 2025

Summary by Sourcery

Introduce webhook enhancements and history sync to WhatsApp Baileys integration, add new API endpoints for instance management and media download, and remove legacy database writes.

New Features:

  • Add initial history sync support by sending combined contacts and messages payload in MESSAGES_SET webhook
  • Expose new endpoints countInstances and instanceByName for instance management
  • Implement downloadMediaMessage endpoint and controller for retrieving media via webhook
  • Introduce quoted message parsing when sending media and audio messages
  • Add isMedia utility function to detect media content in incoming messages
  • Log and apply stored webVersion for WhatsApp Baileys socket initialization

Bug Fixes:

  • Replace deprecated isJidBroadcast check with isJidStatusBroadcast
  • Adjust createJid logic to properly detect group JIDs with corrected length threshold

Enhancements:

  • Set TTL and disable cloning for userDevicesCache in Baileys service
  • Extend sendDataWebhook signature to include historySetData payload
  • Upgrade Docker base image to node:20-alpine3.20, bump package version and update Baileys dependency reference
  • Handle sticker messages in WhatsApp Business integration and streamline contact array mapping

Build:

  • Bump API version in package.json to 2.2.29
  • Update Dockerfile base image, install environment variables for new webhook config
  • Add Prisma migrations for webVersion column and index on IsOnWhatsapp.remoteJid

Chores:

  • Comment out legacy database create/update code for contacts, chats, messages, and media uploads to rely solely on webhooks

jhonsw1 and others added 30 commits November 1, 2024 14:59
Copy link

sourcery-ai bot commented May 13, 2025

Reviewer's Guide

This PR enhances the WhatsApp Baileys integration by logging and overriding the WebVersion dynamically, tightening cache settings, enriching and streamlining webhook payloads (including initial history and group metadata), standardizing media handling, and exposing new instance- and media-download endpoints, alongside cleanup and configuration updates.

File-Level Changes

Change Details Files
Enabled TTL cache for user devices and fetch/stamp WebVersion from DB
  • userDevicesCache now uses a 5-minute TTL and no cloning
  • fetch instance.webVersion from Prisma, log it, split into numeric array
  • override Baileys socketConfig.version with fetched webVersion
whatsapp.baileys.service.ts
Streamlined initial sync to send bundled contacts and messages via webhook
  • commented out legacy chats/contacts batching and DB persistence
  • replaced with single sendDataWebhook call carrying HistorySetData
  • added contacts filter to include only entries with notify/name
whatsapp.baileys.service.ts
wa.types.ts
channel.service.ts
webhook.controller.ts
Augmented sendDataWebhook to include historySetData payload
  • added new optional historySetData param
  • propagate historySetData through EventManager, Webhook/Websocket controllers
channel.service.ts
event.manager.ts
webhook.controller.ts
event.controller.ts
Injected group metadata into outgoing message events
  • introduced groupInfo lookup for group JIDs
  • attached groupInfo (id,subject) to messageRaw in both receive and send flows
whatsapp.baileys.service.ts
Centralized media detection and buffer handling
  • added isMedia helper to detect media across nested message types
  • changed media payloads to use raw Buffer instead of base64 strings
whatsapp.baileys.service.ts
Support quoted messages consistently
  • added parseQuoted to JSON-parse quoted payloads
  • pass parsed quoted into sendNode, media and audio methods
whatsapp.baileys.service.ts
wa.types.ts
baileys.controller.ts
New API endpoints for instance count, lookup, and media download
  • added countInstances and getInstanceByName on InstanceController
  • exposed /instance/countInstances, /instance/instanceByName routes
  • added /downloadMediaMessage endpoint and controller method
instance.controller.ts
instance.router.ts
baileys.router.ts
baileys.controller.ts
Added Meta webhook verification and cleanup routes
  • added GET /webhook/whatsapp challenge flow
  • removed telemetry middleware from root router
meta.router.ts
index.router.ts
Configuration and dependency updates
  • bumped Docker base image to alpine3.20
  • version bump in package.json
  • switched Baileys dependency to WhiskeySockets master
  • added new ENV var WEBHOOK_TEST
Dockerfile
package.json
env.config.ts
Database migrations for webVersion and whatsapp index
  • added migration to add webVersion column to Instance
  • created index on IsOnWhatsapp.remoteJid
prisma-postgresql-migrations/20241105170512_web_version/migration.sql
prisma-postgresql-migrations/20250128200807_added_index_remotejid_on_is_on_whatsapp_table/migration.sql

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

@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 @jhonsw1 - I've reviewed your changes - here's some feedback:

  • Remove large blocks of commented-out logic in the Baileys and Business services or extract them behind feature flags to keep the codebase clean and maintainable.
  • Replace raw console.log/console.error calls with the configured this.logger at the appropriate levels for consistent logging.
  • Ensure the new historySetData parameter added to sendDataWebhook is propagated through all event emitters (webhook, websocket, RabbitMQ) and update their interfaces accordingly.
Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Documentation: all looks good

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.

});
const webVersion = integrationData.webVersion
? integrationData.webVersion.split('.').map(Number)
: version
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Fallback for webVersion uses version string directly

Always convert the fallback version to a number[] (e.g., version.split('.').map(Number)) to avoid passing a string where an array is expected.

received.message?.pollUpdateMessage ||
!received?.message
) {
continue;
return;
Copy link

Choose a reason for hiding this comment

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

issue: Using return inside the loop exits the entire handler

Use continue here so you skip this message but still process the rest of the batch.

@@ -257,7 +257,7 @@ export class ChannelStartupService {
return data;
}

public async sendDataWebhook<T = any>(event: Events, data: T, local = true, integration?: string[]) {
public async sendDataWebhook<T = any>(event: Events, data: T, local = true, integration?: string[], historySetData?: wa.HistorySetData) {
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): New historySetData parameter may not be handled by all event channels

Update WebsocketController and other event emitters to forward historySetData as well; otherwise non-webhook integrations will drop it.

@@ -131,7 +131,8 @@ export class InstanceController {
throw new BadRequestException('number is required');
}
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
webhookWaBusiness = `${urlServer}/webhook/meta`;
const webHookTest = this.configService.get<WaBusiness>('WA_BUSINESS').WEBHOOK_TEST;
webhookWaBusiness = `${webHookTest ? webHookTest : urlServer}/webhook/meta`;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Avoid unneeded ternary statements (simplify-ternary)

Suggested change
webhookWaBusiness = `${webHookTest ? webHookTest : urlServer}/webhook/meta`;
webhookWaBusiness = `${webHookTest || urlServer}/webhook/meta`;


ExplanationIt is possible to simplify certain ternary statements into either use of an || or !.
This makes the code easier to read, since there is no conditional logic.

Comment on lines +300 to +307
const response = {
id: instanceByName.id,
name: instanceByName.name,
ownerJid: instanceByName.ownerJid,
connectionStatus: instanceByName.connectionStatus,
profileName: instanceByName.profileName,
}
return response;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
const response = {
id: instanceByName.id,
name: instanceByName.name,
ownerJid: instanceByName.ownerJid,
connectionStatus: instanceByName.connectionStatus,
profileName: instanceByName.profileName,
}
return response;
return {
id: instanceByName.id,
name: instanceByName.name,
ownerJid: instanceByName.ownerJid,
connectionStatus: instanceByName.connectionStatus,
profileName: instanceByName.profileName,
};


ExplanationSomething that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.

if (!findMessage) {
return;
}
if (item.status === 'read' && key.fromMe) return;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (item.status === 'read' && key.fromMe) return;
if (item.status === 'read' && key.fromMe) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

@@ -954,7 +940,7 @@ export class BusinessStartupService extends ChannelStartupService {
public async mediaMessage(data: SendMediaDto, file?: any) {
const mediaData: SendMediaDto = { ...data };

if (file) mediaData.media = file.buffer.toString('base64');
if (file) mediaData.media = file.buffer;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (file) mediaData.media = file.buffer;
if (file) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

Comment on lines +946 to 951
} else if(received.message?.protocolMessage){
if(!received.message?.protocolMessage?.editedMessage){
this.logger.verbose('message rejected');
return;
}
}
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Merge nested if conditions (merge-nested-ifs)

Suggested change
} else if(received.message?.protocolMessage){
if(!received.message?.protocolMessage?.editedMessage){
this.logger.verbose('message rejected');
return;
}
}
} else if (received.message?.protocolMessage && !received.message?.protocolMessage?.editedMessage) {
this.logger.verbose('message rejected');
return;
}


ExplanationReading deeply nested conditional code is confusing, since you have to keep track of which
conditions relate to which levels. We therefore strive to reduce nesting where
possible, and the situation where two if conditions can be combined using
and is an easy win.


if (file) mediaData.media = file.buffer.toString('base64');
if (file) mediaData.media = file.buffer;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (file) mediaData.media = file.buffer;
if (file) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

},
);

return buffer.toString('base64');;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Remove unreachable code. (remove-unreachable-code)

Suggested change
return buffer.toString('base64');;
return buffer.toString('base64');


ExplanationStatements after a return, break, continue or throw will never be executed.
Leaving them in the code confuses the reader, who may believe that these
statements have some effect. They should therefore be removed.

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