hummingbard/client/link.go

222 lines
4.3 KiB
Go

package client
import (
"context"
"encoding/json"
"log"
"net/http"
"net/url"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/gocolly/colly"
"github.com/gocolly/colly/extensions"
"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
)
func (c *Client) LinkMetadata() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
user, err := c.GetTokenUser(token)
if err != nil || user == nil {
log.Println(err)
w.WriteHeader(http.StatusUnauthorized)
return
}
type payload struct {
Href string `json:"href"`
}
var pay payload
if r.Body == nil {
log.Println(err)
http.Error(w, "Please send a request body", 400)
return
}
err = json.NewDecoder(r.Body).Decode(&pay)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), 400)
return
}
log.Println("recieved payload ", pay)
type Response struct {
Title string `json:"title,omitempty"`
Author string `json:"author,omitempty"`
Description string `json:"description,omitempty"`
Image string `json:"image,omitempty"`
IsYoutube bool `json:"is_youtube,omitempty"`
YoutubeID string `json:"youtube_id,omitempty"`
}
ff := Response{}
up, err := url.Parse(pay.Href)
if err != nil || up == nil || up.Host == "" {
http.Error(w, err.Error(), 400)
return
}
isYoutube := up.Host == "www.youtube.com" || up.Host == "youtube.com"
isShortYoutube := up.Host == "youtu.be"
var title, description, image, author string
if isYoutube || isShortYoutube {
m, err := url.ParseQuery(up.RawQuery)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
id := ""
if isYoutube {
if len(m) > 0 {
id = m["v"][0]
}
} else if isShortYoutube {
if isShortYoutube {
id = up.Path[1:]
}
}
key := c.Config.YoutubeKey
ctx := context.Background()
ctx, _ = context.WithTimeout(ctx, 7*time.Second)
service, err := youtube.NewService(ctx, option.WithAPIKey(key))
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
videos := service.Videos.List([]string{"id", "snippet"})
videos = videos.Id(id)
response, err := videos.Do()
if err != nil {
log.Println(err)
}
if response != nil {
items := response.Items
if items != nil && len(items) >= 1 {
title = items[0].Snippet.Title
description = items[0].Snippet.Description
}
if len(description) > 500 {
description = description[:500]
}
ff.IsYoutube = true
ff.YoutubeID = id
}
} else {
md := c.Scrape(pay.Href, up.Host)
title = md.Title
description = md.Description
author = md.Author
image = md.Image
}
ff.Title = title
ff.Author = author
ff.Description = description
ff.Image = image
js, err := json.Marshal(ff)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
}
}
type LinkMetaData struct {
Title string
Description string
Image string
Author string
}
func (c *Client) Scrape(link string, domain string) LinkMetaData {
co := colly.NewCollector()
extensions.RandomUserAgent(co)
extensions.Referer(co)
htmldata := ""
lmd := LinkMetaData{}
co.OnResponse(func(r *colly.Response) {
htmldata = string(r.Body)
})
co.OnHTML("head", func(e *colly.HTMLElement) {
// Extract meta tags from the document
metaTags := e.DOM.ParentsUntil("~").Find("meta")
metaTags.Each(func(_ int, s *goquery.Selection) {
// Search for og:type meta tags
name, _ := s.Attr("name")
prop, _ := s.Attr("property")
if strings.EqualFold(name, "description") {
description, _ := s.Attr("content")
lmd.Description = description
}
if strings.EqualFold(name, "author") {
author, _ := s.Attr("content")
lmd.Author = author
}
if strings.EqualFold(prop, "og:image") {
image, _ := s.Attr("content")
lmd.Image = image
}
if lmd.Image == "" && strings.EqualFold(prop, "twitter:image:src") {
image, _ := s.Attr("content")
lmd.Image = image
}
})
})
co.OnHTML("head title", func(e *colly.HTMLElement) {
lmd.Title = e.Text
})
co.OnRequest(func(r *colly.Request) {
})
co.Visit(link)
return lmd
}