package businessapi import ( "bytes" "context" "encoding/json" "fmt" "io" "mime/multipart" "net/http" ) // uploadMedia uploads media to the Business API and returns the media ID func (c *Client) uploadMedia(ctx context.Context, data []byte, mimeType string) (string, error) { url := fmt.Sprintf("https://graph.facebook.com/%s/%s/media", c.config.APIVersion, c.config.PhoneNumberID) // Create multipart form data var requestBody bytes.Buffer writer := multipart.NewWriter(&requestBody) // Add the file part, err := writer.CreateFormFile("file", "media") if err != nil { return "", fmt.Errorf("failed to create form file: %w", err) } if _, err := part.Write(data); err != nil { return "", fmt.Errorf("failed to write file data: %w", err) } // Add messaging_product field if err := writer.WriteField("messaging_product", "whatsapp"); err != nil { return "", fmt.Errorf("failed to write messaging_product field: %w", err) } // Add type field (mime type) if err := writer.WriteField("type", mimeType); err != nil { return "", fmt.Errorf("failed to write type field: %w", err) } if err := writer.Close(); err != nil { return "", fmt.Errorf("failed to close multipart writer: %w", err) } // Create request req, err := http.NewRequestWithContext(ctx, "POST", url, &requestBody) if err != nil { return "", fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "Bearer "+c.config.AccessToken) req.Header.Set("Content-Type", writer.FormDataContentType()) // Send request resp, err := c.httpClient.Do(req) if err != nil { return "", fmt.Errorf("failed to upload media: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("failed to read response: %w", err) } if resp.StatusCode != http.StatusOK { var errResp ErrorResponse if err := json.Unmarshal(body, &errResp); err == nil { return "", fmt.Errorf("upload error: %s (code: %d)", errResp.Error.Message, errResp.Error.Code) } return "", fmt.Errorf("upload failed with status %d: %s", resp.StatusCode, string(body)) } var uploadResp MediaUploadResponse if err := json.Unmarshal(body, &uploadResp); err != nil { return "", fmt.Errorf("failed to parse upload response: %w", err) } return uploadResp.ID, nil } // downloadMedia downloads media from the Business API using the media ID func (c *Client) downloadMedia(ctx context.Context, mediaID string) ([]byte, string, error) { // Step 1: Get the media URL url := fmt.Sprintf("https://graph.facebook.com/%s/%s", c.config.APIVersion, mediaID) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, "", fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "Bearer "+c.config.AccessToken) resp, err := c.httpClient.Do(req) if err != nil { return nil, "", fmt.Errorf("failed to get media URL: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, "", fmt.Errorf("failed to get media URL, status %d: %s", resp.StatusCode, string(body)) } var mediaResp MediaURLResponse if err := json.NewDecoder(resp.Body).Decode(&mediaResp); err != nil { return nil, "", fmt.Errorf("failed to parse media URL response: %w", err) } // Step 2: Download from the CDN URL downloadReq, err := http.NewRequestWithContext(ctx, "GET", mediaResp.URL, nil) if err != nil { return nil, "", fmt.Errorf("failed to create download request: %w", err) } downloadReq.Header.Set("Authorization", "Bearer "+c.config.AccessToken) downloadResp, err := c.httpClient.Do(downloadReq) if err != nil { return nil, "", fmt.Errorf("failed to download media: %w", err) } defer downloadResp.Body.Close() if downloadResp.StatusCode != http.StatusOK { return nil, "", fmt.Errorf("failed to download media, status %d", downloadResp.StatusCode) } data, err := io.ReadAll(downloadResp.Body) if err != nil { return nil, "", fmt.Errorf("failed to read media data: %w", err) } return data, mediaResp.MimeType, nil }