* Implement endpoints for managing business profiles: - Get business profile - Update business profile * Add catalog management features: - List catalogs - List products in a catalog - Send catalog messages - Send single product messages - Send product list messages * Introduce media upload functionality for sending media files. * Add flow management capabilities: - Deprecate flows * Update API documentation to reflect new endpoints and features.
173 lines
5.3 KiB
Go
173 lines
5.3 KiB
Go
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) {
|
|
if c.config.BusinessAccountID == "" {
|
|
return nil, errNoBusinessAccount
|
|
}
|
|
|
|
params := url.Values{
|
|
"fields": {"id,name,product_count"},
|
|
}
|
|
|
|
var resp CatalogListResponse
|
|
if err := c.graphAPIGet(ctx, c.config.BusinessAccountID+"/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
|
|
}
|