2020-10-27 17:35:28 +00:00
|
|
|
package urls
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2020-12-18 08:33:24 +00:00
|
|
|
"strings"
|
2020-12-21 13:00:40 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/keighl/metabolize"
|
2020-10-27 17:35:28 +00:00
|
|
|
)
|
|
|
|
|
2021-01-19 15:53:27 +00:00
|
|
|
type YoutubeOembedData struct {
|
2020-10-27 17:35:28 +00:00
|
|
|
ProviderName string `json:"provider_name"`
|
|
|
|
Title string `json:"title"`
|
|
|
|
ThumbnailURL string `json:"thumbnail_url"`
|
|
|
|
}
|
|
|
|
|
2021-01-19 15:53:27 +00:00
|
|
|
type GiphyOembedData struct {
|
|
|
|
ProviderName string `json:"provider_name"`
|
2021-01-20 11:24:41 +00:00
|
|
|
Title string `json:"title"`
|
|
|
|
URL string `json:"url"`
|
2021-01-19 15:53:27 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 11:14:24 +00:00
|
|
|
type TenorOembedData struct {
|
|
|
|
ProviderName string `json:"provider_name"`
|
|
|
|
ThumbnailURL string `json:"thumbnail_url"`
|
|
|
|
AuthorName string `json:"author_name"`
|
|
|
|
}
|
|
|
|
|
2020-10-27 17:35:28 +00:00
|
|
|
type LinkPreviewData struct {
|
2020-12-21 13:00:40 +00:00
|
|
|
Site string `json:"site" meta:"og:site_name"`
|
|
|
|
Title string `json:"title" meta:"og:title"`
|
|
|
|
ThumbnailURL string `json:"thumbnailUrl" meta:"og:image"`
|
2020-12-18 08:33:24 +00:00
|
|
|
ContentType string `json:"contentType"`
|
2020-10-27 17:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Site struct {
|
2020-12-18 08:33:24 +00:00
|
|
|
Title string `json:"title"`
|
|
|
|
Address string `json:"address"`
|
|
|
|
ImageSite bool `json:"imageSite"`
|
2020-10-27 17:35:28 +00:00
|
|
|
}
|
|
|
|
|
2021-01-19 15:53:27 +00:00
|
|
|
const YoutubeOembedLink = "https://www.youtube.com/oembed?format=json&url=%s"
|
|
|
|
const GiphyOembedLink = "https://giphy.com/services/oembed?url=%s"
|
2021-01-20 11:14:24 +00:00
|
|
|
const TenorOembedLink = "https://tenor.com/oembed?url=%s"
|
|
|
|
|
2020-12-21 13:00:40 +00:00
|
|
|
var httpClient = http.Client{
|
|
|
|
Timeout: 30 * time.Second,
|
|
|
|
}
|
|
|
|
|
2020-10-27 17:35:28 +00:00
|
|
|
func LinkPreviewWhitelist() []Site {
|
|
|
|
return []Site{
|
|
|
|
Site{
|
2020-12-18 08:33:24 +00:00
|
|
|
Title: "YouTube",
|
|
|
|
Address: "youtube.com",
|
|
|
|
ImageSite: false,
|
2020-10-27 17:35:28 +00:00
|
|
|
},
|
|
|
|
Site{
|
2020-12-18 08:33:24 +00:00
|
|
|
Title: "YouTube shortener",
|
|
|
|
Address: "youtu.be",
|
|
|
|
ImageSite: false,
|
|
|
|
},
|
|
|
|
Site{
|
|
|
|
Title: "Tenor GIFs",
|
|
|
|
Address: "tenor.com",
|
|
|
|
ImageSite: true,
|
|
|
|
},
|
|
|
|
Site{
|
|
|
|
Title: "GIPHY GIFs",
|
|
|
|
Address: "giphy.com",
|
|
|
|
ImageSite: true,
|
2020-10-27 17:35:28 +00:00
|
|
|
},
|
2020-12-21 13:00:40 +00:00
|
|
|
Site{
|
|
|
|
Title: "GitHub",
|
|
|
|
Address: "github.com",
|
|
|
|
ImageSite: false,
|
|
|
|
},
|
2020-10-27 17:35:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetURLContent(url string) (data []byte, err error) {
|
|
|
|
// nolint: gosec
|
2020-12-21 13:00:40 +00:00
|
|
|
response, err := httpClient.Get(url)
|
2020-10-27 17:35:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return data, fmt.Errorf("Can't get content from link %s", url)
|
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
return ioutil.ReadAll(response.Body)
|
|
|
|
}
|
|
|
|
|
2021-01-19 15:53:27 +00:00
|
|
|
func GetYoutubeOembed(url string) (data YoutubeOembedData, err error) {
|
|
|
|
oembedLink := fmt.Sprintf(YoutubeOembedLink, url)
|
2020-10-27 17:35:28 +00:00
|
|
|
|
|
|
|
jsonBytes, err := GetURLContent(oembedLink)
|
|
|
|
if err != nil {
|
|
|
|
return data, fmt.Errorf("Can't get bytes from youtube oembed response on %s link", oembedLink)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(jsonBytes, &data)
|
|
|
|
if err != nil {
|
|
|
|
return data, fmt.Errorf("Can't unmarshall json")
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetYoutubePreviewData(link string) (previewData LinkPreviewData, err error) {
|
|
|
|
oembedData, err := GetYoutubeOembed(link)
|
|
|
|
if err != nil {
|
|
|
|
return previewData, err
|
|
|
|
}
|
|
|
|
|
|
|
|
previewData.Title = oembedData.Title
|
|
|
|
previewData.Site = oembedData.ProviderName
|
|
|
|
previewData.ThumbnailURL = oembedData.ThumbnailURL
|
|
|
|
|
|
|
|
return previewData, nil
|
|
|
|
}
|
|
|
|
|
2020-12-21 13:00:40 +00:00
|
|
|
func GetGithubPreviewData(link string) (previewData LinkPreviewData, err error) {
|
|
|
|
// nolint: gosec
|
|
|
|
res, err := httpClient.Get(link)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return previewData, fmt.Errorf("Can't get content from link %s", link)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = metabolize.Metabolize(res.Body, &previewData)
|
|
|
|
if err != nil {
|
|
|
|
return previewData, fmt.Errorf("Can't get meta info from link %s", link)
|
|
|
|
}
|
|
|
|
|
|
|
|
return previewData, nil
|
|
|
|
}
|
|
|
|
|
2021-01-19 15:53:27 +00:00
|
|
|
func GetGiphyOembed(url string) (data GiphyOembedData, err error) {
|
|
|
|
oembedLink := fmt.Sprintf(GiphyOembedLink, url)
|
|
|
|
|
|
|
|
jsonBytes, err := GetURLContent(oembedLink)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return data, fmt.Errorf("Can't get bytes from Giphy oembed response at %s", oembedLink)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(jsonBytes, &data)
|
|
|
|
if err != nil {
|
|
|
|
return data, fmt.Errorf("Can't unmarshall json")
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetGiphyPreviewData(link string) (previewData LinkPreviewData, err error) {
|
|
|
|
oembedData, err := GetGiphyOembed(link)
|
|
|
|
if err != nil {
|
|
|
|
return previewData, err
|
|
|
|
}
|
|
|
|
|
|
|
|
previewData.Title = oembedData.Title
|
|
|
|
previewData.Site = oembedData.ProviderName
|
|
|
|
previewData.ThumbnailURL = oembedData.URL
|
|
|
|
|
|
|
|
return previewData, nil
|
|
|
|
}
|
|
|
|
|
2021-01-20 11:14:24 +00:00
|
|
|
func GetTenorOembed(url string) (data TenorOembedData, err error) {
|
|
|
|
oembedLink := fmt.Sprintf(TenorOembedLink, url)
|
|
|
|
|
|
|
|
jsonBytes, err := GetURLContent(oembedLink)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return data, fmt.Errorf("Can't get bytes from Tenor oembed response at %s", oembedLink)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(jsonBytes, &data)
|
|
|
|
if err != nil {
|
|
|
|
return data, fmt.Errorf("Can't unmarshall json")
|
|
|
|
}
|
2020-10-27 17:35:28 +00:00
|
|
|
|
2021-01-20 11:14:24 +00:00
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetTenorPreviewData(link string) (previewData LinkPreviewData, err error) {
|
|
|
|
oembedData, err := GetTenorOembed(link)
|
|
|
|
if err != nil {
|
|
|
|
return previewData, err
|
|
|
|
}
|
|
|
|
|
|
|
|
previewData.Title = oembedData.AuthorName // Tenor Oembed service doesn't return title of the Gif
|
|
|
|
previewData.Site = oembedData.ProviderName
|
|
|
|
previewData.ThumbnailURL = oembedData.ThumbnailURL
|
|
|
|
|
|
|
|
return previewData, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetLinkPreviewData(link string) (previewData LinkPreviewData, err error) {
|
2020-10-27 17:35:28 +00:00
|
|
|
url, err := url.Parse(link)
|
|
|
|
if err != nil {
|
|
|
|
return previewData, fmt.Errorf("Cant't parse link %s", link)
|
|
|
|
}
|
|
|
|
|
2020-12-18 08:33:24 +00:00
|
|
|
hostname := strings.ToLower(url.Hostname())
|
|
|
|
youtubeHostnames := []string{"youtube.com", "www.youtube.com", "youtu.be"}
|
|
|
|
for _, youtubeHostname := range youtubeHostnames {
|
|
|
|
if youtubeHostname == hostname {
|
|
|
|
return GetYoutubePreviewData(link)
|
|
|
|
}
|
|
|
|
}
|
2020-12-21 13:00:40 +00:00
|
|
|
if "github.com" == hostname {
|
|
|
|
return GetGithubPreviewData(link)
|
|
|
|
}
|
2021-01-20 11:14:24 +00:00
|
|
|
if "giphy.com" == hostname {
|
|
|
|
return GetGiphyPreviewData(link)
|
|
|
|
}
|
|
|
|
if "tenor.com" == hostname {
|
|
|
|
return GetTenorPreviewData(link)
|
|
|
|
}
|
2020-12-21 13:00:40 +00:00
|
|
|
|
2020-12-18 08:33:24 +00:00
|
|
|
for _, site := range LinkPreviewWhitelist() {
|
|
|
|
if strings.HasSuffix(hostname, site.Address) && site.ImageSite {
|
|
|
|
content, contentErr := GetURLContent(link)
|
|
|
|
if contentErr != nil {
|
|
|
|
return previewData, contentErr
|
|
|
|
}
|
|
|
|
previewData.ThumbnailURL = link
|
|
|
|
previewData.ContentType = http.DetectContentType(content)
|
|
|
|
return previewData, nil
|
|
|
|
}
|
2020-10-27 17:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return previewData, fmt.Errorf("Link %s isn't whitelisted. Hostname - %s", link, url.Hostname())
|
|
|
|
}
|