fix(product-carousel): address PR review comments
- Replace catalogId with businessOwnerJid (required for catalog reference) - Fix cards structure to use proper IInteractiveMessage[] format - Each card now uses collectionMessage with bizJid and id - Fix body reading from productCarousel.body (was reading from message.body) - Remove 'as any' type casting by using correct proto types - Update examples in types and function documentation Addresses: - Schema mismatch in carousel cards - Body text being silently ignored when nested in productCarousel - Improper type casting masking validation errors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+11
-12
@@ -632,23 +632,23 @@ export type ProductCarouselCard = {
|
|||||||
* ```typescript
|
* ```typescript
|
||||||
* await sock.sendMessage(jid, {
|
* await sock.sendMessage(jid, {
|
||||||
* productCarousel: {
|
* productCarousel: {
|
||||||
* catalogId: '123456789',
|
* businessOwnerJid: '5511999999999@s.whatsapp.net',
|
||||||
* products: [
|
* products: [
|
||||||
* { productId: 'iphone_15' },
|
* { productId: 'iphone_15' },
|
||||||
* { productId: 'macbook_air' },
|
* { productId: 'macbook_air' },
|
||||||
* { productId: 'apple_watch' }
|
* { productId: 'apple_watch' }
|
||||||
* ]
|
* ],
|
||||||
* },
|
* body: 'Check out our featured products!'
|
||||||
* body: 'Check out our featured products!'
|
* }
|
||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export type ProductCarouselMessageOptions = {
|
export type ProductCarouselMessageOptions = {
|
||||||
/** Catalog ID from WhatsApp Business */
|
/** JID of the business owner (catalog owner) - e.g., '5511999999999@s.whatsapp.net' */
|
||||||
catalogId: string
|
businessOwnerJid: string
|
||||||
/** Products to display (2-10 cards required) */
|
/** Products to display (2-10 cards required) */
|
||||||
products: ProductCarouselCard[]
|
products: ProductCarouselCard[]
|
||||||
/** Body text for the message */
|
/** Body text for the carousel message */
|
||||||
body?: string
|
body?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,19 +795,18 @@ export type AnyRegularMessageContent = (
|
|||||||
* ```typescript
|
* ```typescript
|
||||||
* await sock.sendMessage(jid, {
|
* await sock.sendMessage(jid, {
|
||||||
* productCarousel: {
|
* productCarousel: {
|
||||||
* catalogId: '123456789',
|
* businessOwnerJid: '5511999999999@s.whatsapp.net',
|
||||||
* products: [
|
* products: [
|
||||||
* { productId: 'produto_001' },
|
* { productId: 'produto_001' },
|
||||||
* { productId: 'produto_002' },
|
* { productId: 'produto_002' },
|
||||||
* { productId: 'produto_003' }
|
* { productId: 'produto_003' }
|
||||||
* ]
|
* ],
|
||||||
* },
|
* body: 'Confira nossos produtos em destaque!'
|
||||||
* body: 'Confira nossos produtos em destaque!'
|
* }
|
||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
productCarousel: ProductCarouselMessageOptions
|
productCarousel: ProductCarouselMessageOptions
|
||||||
body?: string
|
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
/**
|
/**
|
||||||
|
|||||||
+20
-14
@@ -888,10 +888,13 @@ export const generateProductListMessage = (options: ProductListMessageOptions):
|
|||||||
* Generates a product carousel message using products from WhatsApp Business catalog
|
* Generates a product carousel message using products from WhatsApp Business catalog
|
||||||
* Uses viewOnceMessage wrapper for better iOS/Android compatibility
|
* Uses viewOnceMessage wrapper for better iOS/Android compatibility
|
||||||
*
|
*
|
||||||
|
* Each card in the carousel references a product from the business catalog using
|
||||||
|
* collectionMessage with bizJid (business owner) and id (product ID).
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* const msg = generateProductCarouselMessage({
|
* const msg = generateProductCarouselMessage({
|
||||||
* catalogId: '123456789',
|
* businessOwnerJid: '5511999999999@s.whatsapp.net',
|
||||||
* products: [
|
* products: [
|
||||||
* { productId: 'produto_001' },
|
* { productId: 'produto_001' },
|
||||||
* { productId: 'produto_002' },
|
* { productId: 'produto_002' },
|
||||||
@@ -905,10 +908,10 @@ export const generateProductListMessage = (options: ProductListMessageOptions):
|
|||||||
export const generateProductCarouselMessage = (
|
export const generateProductCarouselMessage = (
|
||||||
options: ProductCarouselMessageOptions
|
options: ProductCarouselMessageOptions
|
||||||
): WAMessageContent => {
|
): WAMessageContent => {
|
||||||
const { catalogId, products, body } = options
|
const { businessOwnerJid, products, body } = options
|
||||||
|
|
||||||
if (!catalogId || typeof catalogId !== 'string' || catalogId.trim().length === 0) {
|
if (!businessOwnerJid || typeof businessOwnerJid !== 'string' || businessOwnerJid.trim().length === 0) {
|
||||||
throw new Boom('catalogId is required and must be a non-empty string', { statusCode: 400 })
|
throw new Boom('businessOwnerJid is required and must be a non-empty string', { statusCode: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!products || products.length < 2) {
|
if (!products || products.length < 2) {
|
||||||
@@ -927,13 +930,16 @@ export const generateProductCarouselMessage = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build cards array with product references
|
// Normalize business owner JID
|
||||||
const cards = products.map((product, index) => ({
|
const normalizedBizJid = jidNormalizedUser(businessOwnerJid)
|
||||||
card_index: index,
|
|
||||||
type: 'product' as const,
|
// Build cards array - each card is an IInteractiveMessage with collectionMessage
|
||||||
action: {
|
// collectionMessage references a product from the business catalog
|
||||||
product_retailer_id: product.productId,
|
const cards: proto.Message.IInteractiveMessage[] = products.map((product) => ({
|
||||||
catalog_id: catalogId
|
collectionMessage: {
|
||||||
|
bizJid: normalizedBizJid,
|
||||||
|
id: product.productId,
|
||||||
|
messageVersion: 1
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -941,7 +947,7 @@ export const generateProductCarouselMessage = (
|
|||||||
const interactiveMessage: proto.Message.IInteractiveMessage = {
|
const interactiveMessage: proto.Message.IInteractiveMessage = {
|
||||||
body: { text: body || '' },
|
body: { text: body || '' },
|
||||||
carouselMessage: {
|
carouselMessage: {
|
||||||
cards: cards as any,
|
cards,
|
||||||
messageVersion: 1
|
messageVersion: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1100,9 +1106,9 @@ export const generateWAMessageContent = async (
|
|||||||
else if (hasNonNullishProperty(message, 'productCarousel')) {
|
else if (hasNonNullishProperty(message, 'productCarousel')) {
|
||||||
const productCarouselMsg = message as any
|
const productCarouselMsg = message as any
|
||||||
const productCarouselOptions: ProductCarouselMessageOptions = {
|
const productCarouselOptions: ProductCarouselMessageOptions = {
|
||||||
catalogId: productCarouselMsg.productCarousel.catalogId,
|
businessOwnerJid: productCarouselMsg.productCarousel.businessOwnerJid,
|
||||||
products: productCarouselMsg.productCarousel.products,
|
products: productCarouselMsg.productCarousel.products,
|
||||||
body: productCarouselMsg.body
|
body: productCarouselMsg.productCarousel.body
|
||||||
}
|
}
|
||||||
const generated = generateProductCarouselMessage(productCarouselOptions)
|
const generated = generateProductCarouselMessage(productCarouselOptions)
|
||||||
m.viewOnceMessage = generated.viewOnceMessage
|
m.viewOnceMessage = generated.viewOnceMessage
|
||||||
|
|||||||
Reference in New Issue
Block a user