package handler import ( "context" "fmt" "github.com/rs/zerolog/log" "github.com/turnage/graw" "net/http" "regexp" "strings" "time" "go.jolheiser.com/lurk/config" "github.com/turnage/graw/reddit" "go.jolheiser.com/disco" ) var httpClient = &http.Client{Timeout: time.Minute} type Reddit struct { Bot reddit.Bot Config *config.Config } func (r *Reddit) Run() error { _, wait, err := graw.Run(r, r.Bot, graw.Config{ Subreddits: r.Config.Reddit.SubRedditNames(), }) if err != nil { return err } return wait() } 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 { log.Debug().Err(err).Msg(p.Subreddit) 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 := &disco.Webhook{ Username: "/r/" + p.Subreddit, AvatarURL: sub.IconURL, Embeds: []*disco.Embed{ { Title: title, URL: p.URL, Description: description, Color: 0x007D96, Timestamp: disco.Now(), Author: &disco.Author{ Name: "/u/" + p.Author, URL: fmt.Sprintf("https://reddit.com/user/%s", p.Author), }, }, }, } if sub.Webhook == "" { log.Error().Msgf("no webhook for %s", p.Subreddit) return nil } req, err := e.Request(context.Background(), sub.Webhook) if err != nil { log.Err(err).Msg("") return nil } resp, err := httpClient.Do(req) if err != nil { log.Err(err).Msg("") return nil } if resp.StatusCode != http.StatusNoContent { log.Error().Msgf(resp.Status) return nil } return nil } func checkPost(c *config.Config, p *reddit.Post) error { sub := c.Reddit.Map[strings.ToLower(p.Subreddit)] // Check blocklist first // Any match means we ignore if matchesAny(p.LinkFlairText, sub.FlairBlocklistRe) { return fmt.Errorf("flair matched blocklisted regex: %s", p.LinkFlairText) } if matchesAny(p.Title, sub.TitleBlocklistRe) { return fmt.Errorf("title matched blocklisted regex: %s", p.Title) } if matchesAny(p.SelfText, sub.BodyBlocklistRe) { return fmt.Errorf("body matched blocklisted regex: %s", p.SelfText) } // Check allowlist // Any match means we pass // If no allowlist, pass if len(sub.FlairAllowlistRe) > 0 && !matchesAny(p.LinkFlairText, sub.FlairAllowlistRe) { return fmt.Errorf("flair didn't match any allowlisted regex: %s", p.LinkFlairText) } if len(sub.TitleAllowlistRe) > 0 && !matchesAny(p.Title, sub.TitleAllowlistRe) { return fmt.Errorf("title didn't match any allowlisted regex: %s", p.Title) } if len(sub.BodyAllowlistRe) > 0 && !matchesAny(p.SelfText, sub.BodyAllowlistRe) { return fmt.Errorf("body didn't match any allowlisted 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 }