github.com/status-im/status-go@v1.1.0/protocol/linkpreview_unfurler_oembed.go (about)

     1  package protocol
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	neturl "net/url"
     8  
     9  	"go.uber.org/zap"
    10  
    11  	"github.com/status-im/status-go/protocol/common"
    12  	"github.com/status-im/status-go/protocol/protobuf"
    13  )
    14  
    15  type OEmbedUnfurler struct {
    16  	logger     *zap.Logger
    17  	httpClient *http.Client
    18  	// oembedEndpoint describes where the consumer may request representations for
    19  	// the supported URL scheme. For example, for YouTube, it is
    20  	// https://www.youtube.com/oembed.
    21  	oembedEndpoint string
    22  	// url is the actual URL to be unfurled.
    23  	url *neturl.URL
    24  }
    25  
    26  func NewOEmbedUnfurler(oembedEndpoint string,
    27  	url *neturl.URL,
    28  	logger *zap.Logger,
    29  	httpClient *http.Client) *OEmbedUnfurler {
    30  	return &OEmbedUnfurler{
    31  		oembedEndpoint: oembedEndpoint,
    32  		url:            url,
    33  		logger:         logger,
    34  		httpClient:     httpClient,
    35  	}
    36  }
    37  
    38  type OEmbedResponse struct {
    39  	Title        string `json:"title"`
    40  	ThumbnailURL string `json:"thumbnail_url"`
    41  }
    42  
    43  func (u *OEmbedUnfurler) newOEmbedURL() (*neturl.URL, error) {
    44  	oembedURL, err := neturl.Parse(u.oembedEndpoint)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	// When format is specified, the provider MUST return data in the requested
    50  	// format, else return an error.
    51  	oembedURL.RawQuery = neturl.Values{
    52  		"url":    {u.url.String()},
    53  		"format": {"json"},
    54  	}.Encode()
    55  
    56  	return oembedURL, nil
    57  }
    58  
    59  func (u OEmbedUnfurler) Unfurl() (*common.LinkPreview, error) {
    60  	preview := newDefaultLinkPreview(u.url)
    61  	preview.Type = protobuf.UnfurledLink_LINK
    62  
    63  	oembedURL, err := u.newOEmbedURL()
    64  	if err != nil {
    65  		return preview, err
    66  	}
    67  
    68  	headers := map[string]string{
    69  		"accept":          headerAcceptJSON,
    70  		"accept-language": headerAcceptLanguage,
    71  		"user-agent":      headerUserAgent,
    72  	}
    73  	oembedBytes, err := fetchBody(u.logger, u.httpClient, oembedURL.String(), headers)
    74  	if err != nil {
    75  		return preview, err
    76  	}
    77  
    78  	var oembedResponse OEmbedResponse
    79  	if err != nil {
    80  		return preview, err
    81  	}
    82  	err = json.Unmarshal(oembedBytes, &oembedResponse)
    83  	if err != nil {
    84  		return preview, err
    85  	}
    86  
    87  	if oembedResponse.Title == "" {
    88  		return preview, fmt.Errorf("missing required title in oEmbed response")
    89  	}
    90  
    91  	preview.Title = oembedResponse.Title
    92  	return preview, nil
    93  }