140 lines
4.0 KiB
Go
140 lines
4.0 KiB
Go
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) (data []byte, mimeType string, err 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)
|
|
}
|
|
|
|
mimeType = mediaResp.MimeType
|
|
return
|
|
}
|