diff --git a/discord.go b/discord.go index 1ac7599..d7ebc1a 100644 --- a/discord.go +++ b/discord.go @@ -3,10 +3,13 @@ package main import ( "bytes" "encoding/json" + "errors" + "fmt" "log" "net/http" "github.com/bwmarrin/discordgo" + "github.com/dustin/go-humanize" "github.com/joho/godotenv" "github.com/namsral/flag" ) @@ -22,12 +25,19 @@ type DiscordEmbedFooter struct { IconUrl string `json:"icon_url"` } +type DiscordEmbedField struct { + Name string `json:"name"` + Value string `json:"value"` + Inline bool `json:"inline"` +} + type DiscordEmbed struct { - Title string `json:"title"` - Url string `json:"url"` - Color int `json:"color"` - Footer DiscordEmbedFooter `json:"footer"` - Timestamp string `json:"timestamp"` + Title string `json:"title"` + Url string `json:"url"` + Color int `json:"color"` + Footer DiscordEmbedFooter `json:"footer"` + Timestamp string `json:"timestamp"` + Fields []DiscordEmbedField `json:"fields"` } type DiscordWebhookPayload struct { @@ -78,7 +88,66 @@ func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) { return } story := FetchStoryById(id) - PostStoryToStackerNews(&story) + id, err = PostStoryToStackerNews(&story) + if err != nil { + var dupesErr *DupesError + if errors.As(err, &dupesErr) { + SendDupesErrorToDiscord(dupesErr) + } else { + log.Fatal("unexpected error returned") + } + } +} + +func SendDupesErrorToDiscord(dupesErr *DupesError) { + title := fmt.Sprintf("%d dupe(s) found for %s:", len(dupesErr.Dupes), dupesErr.Url) + color := 0xffc107 + var fields []DiscordEmbedField + for _, dupe := range dupesErr.Dupes { + fields = append(fields, + DiscordEmbedField{ + Name: "Title", + Value: dupe.Title, + Inline: false, + }, + DiscordEmbedField{ + Name: "Id", + Value: StackerNewsItemLink(dupe.Id), + Inline: true, + }, + DiscordEmbedField{ + Name: "Url", + Value: dupe.Url, + Inline: true, + }, + DiscordEmbedField{ + Name: "User", + Value: dupe.User.Name, + Inline: true, + }, + DiscordEmbedField{ + Name: "Created", + Value: humanize.Time(dupe.CreatedAt), + Inline: true, + }, + DiscordEmbedField{ + Name: "Sats", + Value: fmt.Sprint(dupe.Sats), + Inline: true, + }, + DiscordEmbedField{ + Name: "Comments", + Value: fmt.Sprint(dupe.NComments), + Inline: true, + }, + ) + } + embed := DiscordEmbed{ + Title: title, + Color: color, + Fields: fields, + } + SendEmbedToDiscord(embed) } func SendEmbedToDiscord(embed DiscordEmbed) { diff --git a/sn.go b/sn.go index f3b624e..e080067 100644 --- a/sn.go +++ b/sn.go @@ -18,10 +18,17 @@ type GraphQLPayload struct { Variables map[string]interface{} `json:"variables,omitempty"` } +type SnUser struct { + Name string `json:"name"` +} type Dupe struct { - Id int `json:"id,string"` - Url string `json:"url"` - Title string `json:"title"` + Id int `json:"id,string"` + Url string `json:"url"` + Title string `json:"title"` + User SnUser `json:"user"` + CreatedAt time.Time `json:"createdAt"` + Sats int `json:"sats"` + NComments int `json:"ncomments"` } type DupesResponse struct { @@ -30,6 +37,15 @@ type DupesResponse struct { } `json:"data"` } +type DupesError struct { + Url string + Dupes []Dupe +} + +func (e *DupesError) Error() string { + return fmt.Sprintf("%s has %d dupes", e.Url, len(e.Dupes)) +} + type User struct { Name string `json:"name"` } @@ -123,6 +139,12 @@ func FetchStackerNewsDupes(url string) *[]Dupe { id url title + user { + name + } + createdAt + sats + ncomments } }`, Variables: map[string]interface{}{ @@ -141,11 +163,11 @@ func FetchStackerNewsDupes(url string) *[]Dupe { return &dupesResp.Data.Dupes } -func PostStoryToStackerNews(story *Story) { +func PostStoryToStackerNews(story *Story) (int, error) { dupes := FetchStackerNewsDupes(story.Url) if len(*dupes) > 0 { log.Printf("%s was already posted. Skipping.\n", story.Url) - return + return -1, &DupesError{story.Url, *dupes} } body := GraphQLPayload{ @@ -183,6 +205,7 @@ func PostStoryToStackerNews(story *Story) { story.Score, story.Descendants, ) CommentStackerNewsPost(comment, parentId) + return parentId, nil } func StackerNewsItemLink(id int) string {