From e6bf12fa35eeec4c94dc975a35a0a594eaeca778 Mon Sep 17 00:00:00 2001
From: Manuel <git@huesers.de>
Date: Tue, 17 Oct 2023 00:10:00 +0200
Subject: [PATCH] feat: Add embeds, rework embed struct

---
 cmd/embed.go   | 106 +++++++++++++++++++++++++++++--------------------
 cmd/webhook.go |  77 ++++++++++++++++++++++++++++++-----
 2 files changed, 129 insertions(+), 54 deletions(-)

diff --git a/cmd/embed.go b/cmd/embed.go
index b06d0d2..7740004 100644
--- a/cmd/embed.go
+++ b/cmd/embed.go
@@ -4,18 +4,21 @@ import (
 	"errors"
 	"strconv"
 	"strings"
+	"time"
 )
 
 type Embed struct {
-	Author      Author  `json:"author"`
-	Title       string  `json:"title"`
-	URL         string  `json:"url"`
-	Description string  `json:"description"`
-	Color       int64   `json:"color"`
-	Fields      []Field `json:"fields"`
+	Author      Author  `json:"author,omitempty"`
+	Title       string  `json:"title,omitempty"`
+	URL         string  `json:"url,omitempty"`
+	Description string  `json:"description,omitempty"`
+	Timestamp   string  `json:"timestamp,omitempty"`
+	Color       int64   `json:"color,omitempty"`
+	Fields      []Field `json:"fields,omitempty"`
 	Thumbnail   Image   `json:"thumbnail,omitempty"`
 	Image       Image   `json:"image,omitempty"`
-	Footer      Footer  `json:"footer"`
+	Video       Video   `json:"video,omitempty"`
+	Footer      Footer  `json:"footer,omitempty"`
 }
 
 type Author struct {
@@ -39,60 +42,75 @@ type Image struct {
 	URL string `json:"url"`
 }
 
-func (w *Webhook) NewEmbed(Title, Description, URL string) {
-	emb := Embed{Title: Title, Description: Description, URL: URL}
+type Video struct {
+	URL string `json:"url"`
+}
+
+func (w *Webhook) NewEmbedWithURL(URL string) *Embed {
+	emb := Embed{URL: URL}
 	w.Embeds = append(w.Embeds, &emb)
+	return &emb
 }
 
-func (w *Webhook) SetAuthor(Name, URL, IconURL string) {
-	if len(w.Embeds) == 0 {
-		emb := Embed{Author: Author{Name, URL, IconURL}}
-		w.Embeds = append(w.Embeds, &emb)
-	} else {
-		w.Embeds[0].Author = Author{Name, URL, IconURL}
-	}
+func (w *Webhook) NewEmbed() *Embed {
+	emb := Embed{}
+	w.Embeds = append(w.Embeds, &emb)
+	return &emb
 }
 
-func (w *Webhook) SetColor(color string) error {
+func (e *Embed) SetTitle(Title string) *Embed {
+	e.Title = Title
+	return e
+}
+
+func (e *Embed) SetText(Description string) *Embed {
+	e.Description = Description
+	return e
+}
+
+func (e *Embed) SetAuthor(Name, URL, IconURL string) *Embed {
+	e.Author = Author{Name, URL, IconURL}
+	return e
+}
+
+func (e *Embed) SetColor(color string) (*Embed, error) {
 	color = strings.Replace(color, "0x", "", -1)
 	color = strings.Replace(color, "0X", "", -1)
 	color = strings.Replace(color, "#", "", -1)
 	colorInt, err := strconv.ParseInt(color, 16, 64)
 	if err != nil {
-		return errors.New("Invalid hex code passed")
+		return nil, errors.New("Invalid hex code passed")
 	}
-	w.Embeds[0].Color = colorInt
-	return nil
+	e.Color = colorInt
+	return e, nil
 }
 
-func (w *Webhook) SetThumbnail(URL string) error {
-	if len(w.Embeds) < 1 {
-		return errors.New("Invalid Embed passed in, Webhook.Embeds must have at least one Embed element")
-	}
-	w.Embeds[0].Thumbnail = Image{URL}
-	return nil
+func (e *Embed) SetThumbnail(URL string) *Embed {
+	e.Thumbnail = Image{URL}
+	return e
 }
 
-func (w *Webhook) SetImage(URL string) error {
-	if len(w.Embeds) < 1 {
-		return errors.New("Invalid Embed passed in, Webhook.Embeds must have at least one Embed element")
-	}
-	w.Embeds[0].Image = Image{URL}
-	return nil
+func (e *Embed) SetImage(URL string) *Embed {
+	e.Image = Image{URL}
+	return e
 }
 
-func (w *Webhook) SetFooter(Text, IconURL string) error {
-	if len(w.Embeds) < 1 {
-		return errors.New("Invalid Embed passed in, Webhook.Embeds must have at least one Embed element")
-	}
-	w.Embeds[0].Footer = Footer{Text, IconURL}
-	return nil
+func (e *Embed) SetVideo(URL string) *Embed {
+	e.Video = Video{URL}
+	return e
 }
 
-func (w *Webhook) AddField(Name, Value string, Inline bool) error {
-	if len(w.Embeds) < 1 {
-		return errors.New("Invalid Embed passed in, Webhook.Embeds must have at least one Embed element")
-	}
-	w.Embeds[0].Fields = append(w.Embeds[0].Fields, Field{Name, Value, Inline})
-	return nil
+func (e *Embed) SetFooter(Text, IconURL string) *Embed {
+	e.Footer = Footer{Text, IconURL}
+	return e
+}
+
+func (e *Embed) SetTimestamp(timestamp time.Time) *Embed {
+	e.Timestamp = timestamp.Format(time.RFC3339)
+	return e
+}
+
+func (e *Embed) AddField(Name, Value string, Inline bool) *Embed {
+	e.Fields = append(e.Fields, Field{Name, Value, Inline})
+	return e
 }
diff --git a/cmd/webhook.go b/cmd/webhook.go
index 9969eab..deb9176 100644
--- a/cmd/webhook.go
+++ b/cmd/webhook.go
@@ -6,6 +6,13 @@ import (
 	ts "github.com/n0madic/twitter-scraper"
 	"log"
 	"net/http"
+	"strings"
+	//"strconv"
+)
+
+const (
+	BaseURL  = "https://twitter.com/"
+	BaseIcon = "https://abs.twimg.com/icons/apple-touch-icon-192x192.png"
 )
 
 type Webhook struct {
@@ -25,24 +32,74 @@ type Mention struct {
 
 func sendToWebhook(webhookURL string, tweets []*ts.Tweet) {
 	for _, tweet := range tweets {
-		// Temporarily hardcoded
 		data := Webhook{
-			Content: tweet.PermanentURL,
+			Content: "<" + tweet.PermanentURL + ">",
 			Mention: &Mention{Parse: []string{"roles"}},
-			Embeds:  []*Embed{},
 		}
 
-		jsonData, err := json.Marshal(data)
+		urlsToAppend := []string{}
+		userUrl := BaseURL + tweet.Username
+
+		mainEmbed := data.NewEmbedWithURL(tweet.PermanentURL)
+		mainEmbed.SetAuthor(tweet.Name+" (@"+tweet.Username+")", userUrl, "https://unavatar.io/twitter/"+tweet.Username)
+		mainEmbed.SetText(tweet.Text)
+		mainEmbed.SetColor("#26a7de")
+		mainEmbed.SetFooter("Twitter", BaseIcon)
+		mainEmbed.SetTimestamp(tweet.TimeParsed)
+		//mainEmbed.SetFooter("Twitter • <t:" + strconv.FormatInt((tweet.Timestamp), 10) + ":R>", BaseIcon)
+
+		for i, photo := range tweet.Photos {
+			embed := mainEmbed
+			if i > 0 {
+				embed = data.NewEmbedWithURL(tweet.PermanentURL)
+			}
+			embed.SetImage(photo.URL)
+		}
+		for i, gif := range tweet.GIFs {
+			embed := mainEmbed
+			if i > 0 {
+				embed = data.NewEmbedWithURL(tweet.PermanentURL)
+			}
+			embed.SetImage(gif.Preview)
+		}
+		for i, video := range tweet.Videos {
+			embed := mainEmbed
+			if i > 0 {
+				embed = data.NewEmbedWithURL(tweet.PermanentURL)
+			}
+			// Video embeds are not supported right now
+			embed.SetImage(video.Preview)
+			embed.SetVideo(video.URL) // This has sadly no effect
+			urlsToAppend = append(urlsToAppend, strings.Replace(tweet.PermanentURL, "twitter", "fxtwitter", 1))
+		}
+
+		err := sendRequest(webhookURL, &data)
 		if err != nil {
-			log.Printf("Error while generating JSON for tweet %s: %s", tweet.ID, err.Error())
+			log.Println("Error while sending webhook for tweet %s: %s", tweet.ID, err.Error())
 			continue
 		}
 
-		resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
-		if err != nil {
-			log.Printf("Error while sending webhook for tweet %s: %s", tweet.ID, err.Error())
-			continue
+		for _, url := range urlsToAppend {
+			err := sendRequest(webhookURL, &Webhook{Content: url})
+			if err != nil {
+				log.Println("Error while sending webhook for tweet %s: %s", tweet.ID, err.Error())
+				continue
+			}
 		}
-		defer resp.Body.Close()
 	}
 }
+
+func sendRequest(url string, data *Webhook) error {
+	jsonData, err := json.Marshal(data)
+	if err != nil {
+		return err
+	}
+
+	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
+	defer resp.Body.Close()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}