package businessapi import ( "context" "net/url" "git.warky.dev/wdevs/whatshooked/pkg/events" "git.warky.dev/wdevs/whatshooked/pkg/logging" "go.mau.fi/whatsmeow/types" ) // ListCatalogs returns all product catalogs linked to the business account. func (c *Client) ListCatalogs(ctx context.Context) (*CatalogListResponse, error) { wabaID, err := c.resolveWABAID(ctx) if err != nil { return nil, err } params := url.Values{ "fields": {"id,name,product_count"}, } var resp CatalogListResponse if err := c.graphAPIGet(ctx, wabaID+"/product_catalogs", params, &resp); err != nil { return nil, err } return &resp, nil } // ListProducts returns products in a specific catalog. func (c *Client) ListProducts(ctx context.Context, catalogID string) (*ProductListResponse, error) { params := url.Values{ "fields": {"product_retailer_id,name,description,image_url,base_price,currency,availability,category"}, } var resp ProductListResponse if err := c.graphAPIGet(ctx, catalogID+"/products", params, &resp); err != nil { return nil, err } return &resp, nil } // SendCatalogMessage sends a catalog message that shares the full product catalog. // thumbnailProductRetailerID is optional — when non-empty it sets which product image // appears as the catalog preview thumbnail. func (c *Client) SendCatalogMessage(ctx context.Context, jid types.JID, bodyText string, thumbnailProductRetailerID string) (string, error) { if ctx == nil { ctx = context.Background() } phoneNumber := jidToPhoneNumber(jid) action := map[string]any{ "name": "catalog_message", } if thumbnailProductRetailerID != "" { action["parameters"] = map[string]any{ "thumbnail_product_retailer_id": thumbnailProductRetailerID, } } msg := map[string]any{ "messaging_product": "whatsapp", "to": phoneNumber, "type": "interactive", "interactive": map[string]any{ "type": "catalog_message", "body": map[string]any{"text": bodyText}, "action": action, }, } messageID, err := c.postToMessagesEndpoint(ctx, msg) if err != nil { c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, bodyText, err)) return "", err } logging.Debug("Catalog message sent via Business API", "account_id", c.id, "to", phoneNumber) c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, bodyText)) return messageID, nil } // SendSingleProduct sends a single-product interactive message. func (c *Client) SendSingleProduct(ctx context.Context, jid types.JID, catalogID, productRetailerID, bodyText, footerText string) (string, error) { if ctx == nil { ctx = context.Background() } phoneNumber := jidToPhoneNumber(jid) interactive := map[string]any{ "type": "product", "header": map[string]any{ "type": "product", "product_retailer_id": productRetailerID, }, "body": map[string]any{"text": bodyText}, "action": map[string]any{ "catalog_id": catalogID, "product_retailer_id": productRetailerID, }, } if footerText != "" { interactive["footer"] = map[string]any{"text": footerText} } msg := map[string]any{ "messaging_product": "whatsapp", "to": phoneNumber, "type": "interactive", "interactive": interactive, } messageID, err := c.postToMessagesEndpoint(ctx, msg) if err != nil { c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, bodyText, err)) return "", err } logging.Debug("Single product sent via Business API", "account_id", c.id, "to", phoneNumber, "product", productRetailerID) c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, bodyText)) return messageID, nil } // SendProductList sends a multi-product list message. Up to 30 products across up to 10 sections. func (c *Client) SendProductList(ctx context.Context, jid types.JID, headerText, bodyText, footerText, catalogID string, sections []ProductListSection) (string, error) { if ctx == nil { ctx = context.Background() } phoneNumber := jidToPhoneNumber(jid) actionSections := make([]map[string]any, len(sections)) for i, s := range sections { items := make([]map[string]any, len(s.ProductItems)) for j, item := range s.ProductItems { items[j] = map[string]any{"product_retailer_id": item.ProductRetailerID} } actionSections[i] = map[string]any{ "title": s.Title, "product_items": items, } } interactive := map[string]any{ "type": "product_list", "header": map[string]any{"type": "text", "text": headerText}, "body": map[string]any{"text": bodyText}, "action": map[string]any{ "catalog_id": catalogID, "sections": actionSections, }, } if footerText != "" { interactive["footer"] = map[string]any{"text": footerText} } msg := map[string]any{ "messaging_product": "whatsapp", "to": phoneNumber, "type": "interactive", "interactive": interactive, } messageID, err := c.postToMessagesEndpoint(ctx, msg) if err != nil { c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, bodyText, err)) return "", err } logging.Debug("Product list sent via Business API", "account_id", c.id, "to", phoneNumber, "sections", len(sections)) c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, bodyText)) return messageID, nil }