From d98f4050104c19e43e7f732d6c83db231b7d7460 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 3 Jan 2024 13:36:03 +0100 Subject: [PATCH] youtube: Add alternative non-clickbait titles powered by DeArrow --- plugin/youtube/api.go | 39 ++++++++++++++++++++++++++++ plugin/youtube/youtube.go | 51 ++++++++++++++++++++++++++++++++++++- plugin/youtube/youtube.http | 6 ++++- 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/plugin/youtube/api.go b/plugin/youtube/api.go index 1c05ead..c109792 100644 --- a/plugin/youtube/api.go +++ b/plugin/youtube/api.go @@ -53,6 +53,15 @@ type ( Blocked []string `json:"blocked"` } `json:"regionRestriction"` } + + DeArrowResponse struct { + Titles []struct { + Title string `json:"title"` + Original bool `json:"original"` + Votes int `json:"votes"` + Locked bool `json:"locked"` + } `json:"titles"` + } ) func (v *Video) BlockedInGermany() bool { @@ -91,3 +100,33 @@ func (v *Video) WasLive() bool { func (c *ContentDetails) ParseDuration() (*duration.Duration, error) { return duration.Parse(c.Duration) } + +func (d *DeArrowResponse) GetBestTitle() string { + // From the API docs: + // "Data is returned ordered. You can use the first element. However, you should make sure the first element + // has either locked = true or votes >= 0. If not, it is considered untrusted and is only to be shown + // in the voting box until it has been confirmed by another user." + + if len(d.Titles) == 0 { + return "" + } + + if d.Titles[0].Locked && !d.Titles[0].Original { + return d.Titles[0].Title + } + + // Will check for the highest number of votes instead of just taking the first one. + // API docs say that you should not use titles without votes but the browser extension does it anyway. + // So we will do it too! + maxVotes := -1 + var title string + for _, t := range d.Titles { + t := t + if t.Votes > maxVotes && !t.Original { + maxVotes = t.Votes + title = t.Title + } + } + + return title +} diff --git a/plugin/youtube/youtube.go b/plugin/youtube/youtube.go index 198e984..8391af4 100644 --- a/plugin/youtube/youtube.go +++ b/plugin/youtube/youtube.go @@ -103,6 +103,42 @@ func (p *Plugin) getVideoInfo(videoID string) (Video, error) { return response.Items[0], nil } +func deArrow(originalText string, video *Video) (string, error) { + // https://wiki.sponsor.ajay.app/w/API_Docs/DeArrow#GET_/api/branding + deArrowUrl := fmt.Sprintf("https://sponsor.ajay.app/api/branding/?videoID=%s", video.ID) + var deArrowResponse DeArrowResponse + var httpError *httpUtils.HttpError + err := httpUtils.GetRequest( + deArrowUrl, + &deArrowResponse, + ) + + if err != nil { + if errors.As(err, &httpError) { + if httpError.StatusCode == 500 { // API seems to throw 500 for every empty response + return "", nil + } + } + return "", err + } + + alternativeTitle := deArrowResponse.GetBestTitle() + if alternativeTitle != "" { + modifiedText := strings.Replace( + originalText, + fmt.Sprintf("%s\n", utils.Escape(video.Snippet.Title)), + fmt.Sprintf("%s\nAlternativer Titel: %s\n", + utils.Escape(video.Snippet.Title), + utils.Escape(alternativeTitle), + ), + 1, + ) + return modifiedText, nil + } + + return "", nil +} + func constructText(video *Video) string { var sb strings.Builder @@ -276,7 +312,20 @@ func (p *Plugin) OnYouTubeLink(c plugin.GobotContext) error { text := constructText(&video) - return c.Reply(text, utils.DefaultSendOptions) + msg, err := c.Bot().Reply(c.Message(), text, utils.DefaultSendOptions) + if err == nil { + modifiedText, err := deArrow(text, &video) + if err != nil { + log.Err(err). + Str("videoID", videoID). + Msg("Error while contacting DeArrow API") + return nil + } + + _, err = c.Bot().Edit(msg, modifiedText, utils.DefaultSendOptions) + } + + return err } func (p *Plugin) onYouTubeSearch(c plugin.GobotContext) error { diff --git a/plugin/youtube/youtube.http b/plugin/youtube/youtube.http index f240de5..c2c9c34 100644 --- a/plugin/youtube/youtube.http +++ b/plugin/youtube/youtube.http @@ -26,4 +26,8 @@ GET https://www.googleapis.com/youtube/v3/search &part=snippet &maxResults=1 &type=video - &fields=items/id/videoId \ No newline at end of file + &fields=items/id/videoId + +### DeArrow +GET https://sponsor.ajay.app/api/branding/ + ?videoID=7DKv5H5Frt0 \ No newline at end of file