feat(chat): add whatsapp number check guardrails#2581
Open
nikolasdehor wants to merge 3 commits into
Open
Conversation
Contributor
Reviewer's GuideImplements abuse-safety guardrails for the /chat/whatsappNumbers endpoint by enforcing configurable batch limits, chunking Baileys onWhatsApp checks with pacing, tightening request validation, propagating 429 responses with Retry-After, and documenting responsible messaging expectations. Sequence diagram for whatsappNumbers guardrails and Baileys chunked checkssequenceDiagram
actor Client
participant ChatRouter
participant BaileysStartupService
participant ConfigService
participant BaileysClient
Client->>ChatRouter: POST /chat/whatsappNumbers
ChatRouter->>BaileysStartupService: whatsappNumber(WhatsAppNumberDto)
BaileysStartupService->>BaileysStartupService: getWhatsappNumbersGuardrails()
BaileysStartupService->>ConfigService: get ABUSE_SAFETY
ConfigService-->>BaileysStartupService: AbuseSafety
BaileysStartupService->>BaileysStartupService: validate numbers required
alt [numbers.length > guardrails.maxBatchSize]
BaileysStartupService->>BaileysStartupService: TooManyRequestsException(retryAfter, details)
BaileysStartupService-->>ChatRouter: error { status 429, retryAfter }
ChatRouter->>ChatRouter: set Retry-After header
ChatRouter-->>Client: 429 Too Many Requests
else [numbers.length <= guardrails.maxBatchSize]
BaileysStartupService->>BaileysStartupService: compute normalNumbersNotInCache
opt [normalNumbersNotInCache.length > 0]
BaileysStartupService->>BaileysStartupService: queryOnWhatsappInChunks(numbers, batchSize, intervalMs)
loop for each chunk
BaileysStartupService->>BaileysClient: onWhatsApp(...chunk)
BaileysClient-->>BaileysStartupService: results
alt [hasMoreChunks && intervalMs > 0]
BaileysStartupService->>BaileysStartupService: delay(intervalMs)
end
end
end
BaileysStartupService-->>ChatRouter: whatsappNumber response
ChatRouter-->>Client: 200 OK
end
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The
TooManyRequestsExceptionclass throws a plain object from its constructor rather than extendingError, which prevents stack traces and consistent error handling; consider making it anErrorsubclass and instantiating (not throwing) it where needed, similar to other exception patterns. - In
whatsappNumber,numbersis derived withArray.isArray(data?.numbers) ? data.numbers : []even though the schema now requiresnumbers; you could simplify this and/or explicitly handle non-array input with a clearer validation error rather than silently treating it as an empty array. - The updated
chat.routererror handler now returns the entireerrorobject to clients; review which fields are exposed and consider normalizing the response shape to avoid leaking internal or unexpected properties from thrown objects.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `TooManyRequestsException` class throws a plain object from its constructor rather than extending `Error`, which prevents stack traces and consistent error handling; consider making it an `Error` subclass and instantiating (not throwing) it where needed, similar to other exception patterns.
- In `whatsappNumber`, `numbers` is derived with `Array.isArray(data?.numbers) ? data.numbers : []` even though the schema now requires `numbers`; you could simplify this and/or explicitly handle non-array input with a clearer validation error rather than silently treating it as an empty array.
- The updated `chat.router` error handler now returns the entire `error` object to clients; review which fields are exposed and consider normalizing the response shape to avoid leaking internal or unexpected properties from thrown objects.
## Individual Comments
### Comment 1
<location path="src/api/routes/chat.router.ts" line_range="65-69" />
<code_context>
} catch (error) {
console.log(error);
- return res.status(HttpStatus.BAD_REQUEST).json(error);
+ if (error?.retryAfter) {
+ res.set('Retry-After', String(error.retryAfter));
+ }
+
+ return res.status(error?.status || HttpStatus.BAD_REQUEST).json(error);
}
})
</code_context>
<issue_to_address>
**issue:** The conditional on `retryAfter` skips sending `Retry-After` when it is `0`, which might be unintended for future uses.
`Retry-After` is only set when `error.retryAfter` is truthy. The current caller always uses ≥1s, but `TooManyRequestsException` permits `retryAfter` to be `0` or omitted. If a future caller intentionally passes `0`, the header will be skipped, which may be unexpected. To treat `0` as valid, check `error.retryAfter != null` instead of relying on truthiness.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Author
|
Addressed the Sourcery review in 7af44d9:
Validated locally:
@sourcery-ai review |
Author
|
Added a scoped test/observability follow-up in e8b6531:
Validated locally:
Sidecar read-only code review also found no blocking issues. @sourcery-ai review |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds abuse-safety guardrails for the
POST /chat/whatsappNumbers/{instance}endpoint.This endpoint can call Baileys
onWhatsAppfor uncached numbers. When callers submit large batches, the instance can generate a burst of WhatsApp Web checks. This PR adds bounded, configurable backpressure for that path and documents responsible messaging expectations.What Changed
whatsappNumbersguardrails:ABUSE_SAFETY_WHATSAPP_NUMBERS_MAX_BATCH_SIZEABUSE_SAFETY_WHATSAPP_NUMBERS_QUERY_BATCH_SIZEABUSE_SAFETY_WHATSAPP_NUMBERS_QUERY_BATCH_INTERVAL_MS429 Too Many RequestsplusRetry-Afterwhen a request exceeds the configured max batch size.onWhatsAppchecks for uncached numbers instead of sending all checks in a single call.numbersin the request schema to avoid accidental malformed calls.docs/responsible-messaging.mdand links it from the README.References
/chat/whatsappNumbers.Scope Notes
This is not an anti-ban feature and does not guarantee delivery or account safety.
Intentionally out of scope:
The goal is narrower: prevent accidental bursts on a sensitive endpoint, provide clear 429 feedback, and document responsible operation.
Validation
DATABASE_PROVIDER=postgresql npm run db:generatenpm run lint:checknpm run buildSummary by Sourcery
Add configurable abuse-safety guardrails for the /chat/whatsappNumbers endpoint, including batch limiting, chunked Baileys checks, and clearer error handling and documentation around responsible messaging.
New Features:
Enhancements:
Documentation: