2021-03-24 01:28:06 +00:00
|
|
|
package handler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"go.jolheiser.com/lurk/config"
|
|
|
|
|
2022-11-08 04:23:53 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/turnage/graw"
|
2021-03-24 01:28:06 +00:00
|
|
|
"github.com/turnage/graw/reddit"
|
|
|
|
"go.jolheiser.com/disco"
|
|
|
|
)
|
|
|
|
|
|
|
|
var httpClient = &http.Client{Timeout: time.Minute}
|
|
|
|
|
|
|
|
type Reddit struct {
|
2021-12-17 04:40:07 +00:00
|
|
|
Bot reddit.Bot
|
2021-03-24 01:28:06 +00:00
|
|
|
Config *config.Config
|
|
|
|
}
|
|
|
|
|
2021-12-17 04:40:07 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2021-03-24 01:28:06 +00:00
|
|
|
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 {
|
2021-12-17 04:40:07 +00:00
|
|
|
log.Debug().Err(err).Msg(p.Subreddit)
|
2021-03-24 01:28:06 +00:00
|
|
|
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] + "..."
|
|
|
|
}
|
|
|
|
|
2021-03-24 03:21:41 +00:00
|
|
|
e := &disco.Webhook{
|
2021-03-24 01:28:06 +00:00
|
|
|
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),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-11-08 04:23:53 +00:00
|
|
|
for _, webhook := range sub.Webhooks {
|
|
|
|
req, err := e.Request(context.Background(), webhook)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err).Msg("")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := httpClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err).Msg("")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusNoContent {
|
|
|
|
log.Error().Msgf(resp.Status)
|
|
|
|
continue
|
|
|
|
}
|
2021-03-24 01:28:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|