package handler import ( "bytes" "encoding/json" "fmt" "net/http" "regexp" "strings" "time" "go.etztech.xyz/lurk/config" "github.com/turnage/graw/reddit" "go.jolheiser.com/beaver" ) var httpClient = &http.Client{Timeout: time.Minute} type Webhook struct { Username string `json:"username"` AvatarURL string `json:"avatar_url"` Embeds []Embed `json:"embeds"` } type Embed struct { Title string `json:"title"` URL string `json:"url"` Description string `json:"description"` Color int64 `json:"color"` Timestamp time.Time `json:"timestamp"` Author Author `json:"author"` } type Author struct { Name string `json:"name"` URL string `json:"url"` } type Reddit struct { Config *config.Config } func (r *Reddit) Post(p *reddit.Post) error { sub := r.Config.Reddit.Map[strings.ToLower(p.Subreddit)] if err := checkPost(r.Config, p); err != nil { beaver.Debugf("%s: %v", p.Subreddit, err) return nil } title := p.Title if len(title) > sub.TitleLimit { title = title[:sub.TitleLimit] + "..." } description := p.SelfText if len(description) > sub.BodyLimit { description = description[:sub.BodyLimit] + "..." } e := Webhook{ Username: "/r/" + p.Subreddit, AvatarURL: sub.IconURL, Embeds: []Embed{ { Title: title, URL: p.URL, Description: description, Color: 16312092, // Yellow Timestamp: time.Now(), Author: Author{ Name: "/u/" + p.Author, URL: fmt.Sprintf("https://reddit.com/user/%s", p.Author), }, }, }, } data, err := json.Marshal(e) if err != nil { beaver.Error(err) return nil } beaver.Debug(string(data)) if sub.Webhook == "" { beaver.Errorf("no webhook for %s", p.Subreddit) return nil } payload := bytes.NewBuffer(data) resp, err := httpClient.Post(sub.Webhook, "application/json", payload) if err != nil { beaver.Error(err) return nil } if resp.StatusCode != http.StatusNoContent { beaver.Error(resp.Status) return nil } return nil } func checkPost(c *config.Config, p *reddit.Post) error { sub := c.Reddit.Map[strings.ToLower(p.Subreddit)] // Check blacklist first // Any match means we ignore if matchesAny(p.LinkFlairText, sub.FlairBlacklistRe) { return fmt.Errorf("flair matched blacklisted regex: %s", p.LinkFlairText) } if matchesAny(p.Title, sub.TitleBlacklistRe) { return fmt.Errorf("title matched blacklisted regex: %s", p.Title) } if matchesAny(p.SelfText, sub.BodyBlacklistRe) { return fmt.Errorf("body matched blacklisted regex: %s", p.SelfText) } // Check whitelist // Any match means we pass // If no whitelist, pass if len(sub.FlairWhitelistRe) > 0 && !matchesAny(p.LinkFlairText, sub.FlairWhitelistRe) { return fmt.Errorf("flair didn't match any whitelisted regex: %s", p.LinkFlairText) } if len(sub.TitleWhitelistRe) > 0 && !matchesAny(p.Title, sub.TitleWhitelistRe) { return fmt.Errorf("title didn't match any whitelisted regex: %s", p.Title) } if len(sub.BodyWhitelistRe) > 0 && !matchesAny(p.SelfText, sub.BodyWhitelistRe) { return fmt.Errorf("body didn't match any whitelisted regex: %s", p.SelfText) } return nil } func matchesAny(input string, re []*regexp.Regexp) bool { for _, r := range re { if r.MatchString(input) { return true } } return false }