lurk/handler/reddit.go

144 lines
3.3 KiB
Go
Raw Permalink Normal View History

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
}