github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/unfurl/scrape_giphy.go (about) 1 package unfurl 2 3 import ( 4 "context" 5 "errors" 6 "net/http" 7 "net/url" 8 "strconv" 9 "strings" 10 11 "github.com/keybase/client/go/libkb" 12 13 "github.com/gocolly/colly/v2" 14 "github.com/keybase/client/go/chat/giphy" 15 "github.com/keybase/client/go/protocol/chat1" 16 ) 17 18 var giphyFavicon = "https://giphy.com/static/img/icons/apple-touch-icon-180px.png" 19 20 func (s *Scraper) scrapeGiphyWithMetadata(ctx context.Context, sourceURL string) (res chat1.UnfurlRaw, err error) { 21 defer s.Trace(ctx, &err, "scrapeGiphyWithMetadata")() 22 url, err := url.Parse(sourceURL) 23 if err != nil { 24 return res, err 25 } 26 if url.Fragment == "" { 27 return res, errors.New("no fragment") 28 } 29 toks := strings.Split(url.Fragment, "&") 30 if len(toks) != 3 { 31 return res, errors.New("not enough params") 32 } 33 var rawgiphy chat1.UnfurlGiphyRaw 34 var video chat1.UnfurlVideo 35 var height, width int64 36 isVideo := false 37 for _, tok := range toks { 38 vals := strings.Split(tok, "=") 39 if len(vals) != 2 { 40 return res, errors.New("invalid val") 41 } 42 switch vals[0] { 43 case "height": 44 if height, err = strconv.ParseInt(vals[1], 0, 0); err != nil { 45 return res, err 46 } 47 case "width": 48 if width, err = strconv.ParseInt(vals[1], 0, 0); err != nil { 49 return res, err 50 } 51 case "isvideo": 52 if isVideo, err = strconv.ParseBool(vals[1]); err != nil { 53 return res, err 54 } 55 } 56 } 57 rawgiphy.FaviconUrl = &giphyFavicon 58 if isVideo { 59 video.Height = int(height) 60 video.Width = int(width) 61 video.Url = sourceURL 62 video.MimeType = "video/mp4" 63 rawgiphy.Video = &video 64 } else { 65 rawgiphy.ImageUrl = &sourceURL 66 } 67 return chat1.NewUnfurlRawWithGiphy(rawgiphy), nil 68 } 69 70 func (s *Scraper) scrapeGiphy(ctx context.Context, sourceURL string) (res chat1.UnfurlRaw, err error) { 71 defer s.Trace(ctx, &err, "scrapeGiphy")() 72 if res, err = s.scrapeGiphyWithMetadata(ctx, sourceURL); err == nil { 73 s.Debug(ctx, "scrapeGiphy: successfully scraped with metadata") 74 return res, nil 75 } 76 77 c := s.makeCollector() 78 var rawgiphy chat1.UnfurlGiphyRaw 79 var video chat1.UnfurlVideo 80 video.MimeType = "video/mp4" 81 generic := new(scoredGenericRaw) 82 if err = s.addGenericScraperToCollector(ctx, c, generic, sourceURL, "giphy.com"); err != nil { 83 return res, err 84 } 85 86 c.OnHTML("head meta[content][property]", func(e *colly.HTMLElement) { 87 attr := strings.ToLower(e.Attr("property")) 88 if attr == "og:video" { 89 video.Url = e.Attr("content") 90 } else if attr == "og:video:width" { 91 if width, err := strconv.Atoi(e.Attr("content")); err == nil { 92 video.Width = width 93 } 94 95 } else if attr == "og:video:height" { 96 if height, err := strconv.Atoi(e.Attr("content")); err == nil { 97 video.Height = height 98 } 99 } else { 100 s.setAttr(ctx, attr, "giphy.com", "giphy.com", generic, e) 101 } 102 }) 103 var uri string 104 if s.giphyProxy { 105 c.WithTransport(giphy.WebClient(libkb.NewMetaContext(ctx, s.G().ExternalG())).Transport) 106 if uri, err = giphy.ProxyURL(sourceURL); err != nil { 107 return res, err 108 } 109 } else { 110 uri = sourceURL 111 } 112 hdr := make(http.Header) 113 hdr.Add("Host", giphy.Host) 114 hdr.Add("Accept", "*/*") 115 hdr.Add("Connection", "keep-alive") 116 hdr.Add("upgrade-insecure-requests", "1") 117 hdr.Add("user-agent", userAgent) 118 if err := c.Request("GET", uri, nil, nil, hdr); err != nil { 119 return res, err 120 } 121 if generic.ImageUrl == nil { 122 // If we couldn't find an image, then just return the generic 123 s.Debug(ctx, "scrapeGiphy: failed to find an image, just returning generic unfurl") 124 return s.exportGenericResult(generic) 125 } 126 if len(video.Url) > 0 && video.Height > 0 && video.Width > 0 { 127 rawgiphy.Video = &video 128 } 129 rawgiphy.ImageUrl = generic.ImageUrl 130 rawgiphy.FaviconUrl = generic.FaviconUrl 131 return chat1.NewUnfurlRawWithGiphy(rawgiphy), nil 132 }