144 lines
3.3 KiB
Go
144 lines
3.3 KiB
Go
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
|
|
}
|