acdform/actions.go

282 lines
6.5 KiB
Go

package acdform
import (
"crypto/md5"
"encoding/base64"
"fmt"
"io"
"log"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"codeberg.org/eviedelta/dwhook"
)
var picrewRegex = regexp.MustCompile(`^https?\:\/\/picrew\.me\/image_maker\/(\d+)$`)
var thumbExtractor = regexp.MustCompile(`\<meta[^\>]*property=\"(?:og\:)?image\" content=\"(https?\:\/\/\w+\.\w+\/[^>]+\.(?:png|jpeg|jpg))\"\>`)
var nameExtractor = regexp.MustCompile(`\<meta[^\>]*property=\"(?:og\:)?title\" content=\"([^">]+)\"\>`)
func getThumbURL(uri string) (thumb string, name string) {
purl, err := url.Parse(uri)
if err != nil {
log.Println(err)
return
}
if Conf.ThumbnailLetlist != nil {
for _, x := range Conf.ThumbnailLetlist {
if purl.Host == x {
goto pass
}
}
return
}
pass:
if EnableThumbs {
mapMu.Lock()
defer mapMu.Unlock()
if !thumbRatelimit[uri].IsZero() && time.Now().Sub(thumbRatelimit[uri]) < time.Hour*24 {
id, err := GetMakerID(uri)
if err != nil {
return
}
thumb = Conf.BaseURL + "/media/" + strconv.Itoa(id) + ".jpeg"
name = nameCache[uri]
return
}
thumbRatelimit[uri] = time.Now()
}
req, err := http.NewRequest("GET", uri, nil)
if err != nil {
log.Println(err)
return "", ""
}
req.Header.Set("user-agent", "Thumbnail Fetcher for ACD Submissions")
req.Header.Set("from", Conf.HostEmail)
bod, err := http.DefaultClient.Do(req)
if err != nil {
log.Println(err)
return "", ""
}
defer bod.Body.Close()
dat, err := io.ReadAll(bod.Body)
if err != nil {
log.Println(err)
return "", ""
}
if thumbExtractor.Match(dat) {
thumb = thumbExtractor.FindStringSubmatch(string(dat))[1]
if EnableThumbs && (strings.HasSuffix(thumb, ".png") || strings.HasSuffix(thumb, ".jpeg") || strings.HasSuffix(thumb, ".jpg")) {
go addThumbnail(uri, thumb)
}
}
if nameExtractor.Match(dat) {
name = nameExtractor.FindStringSubmatch(string(dat))[1]
nameCache[uri] = name
}
if EnableThumbs {
thumbRatelimit[uri] = time.Now()
}
return
}
func newNotification(s Submission, where string) {
ifdef := func(s, def string) string {
if s == "" {
return def
}
return s
}
title := s.FmtName()
thumb, name := getThumbURL(s.URI)
// fmt.Println(thumb, name)
if Hook != nil {
err := func() error {
fields := []dwhook.EmbedField{{
Name: "Name",
Value: "> " + name,
}, {
Name: "Tags",
Value: "> " + s.Tags,
}}
if where != "" {
fields = append(fields, dwhook.EmbedField{
Name: "Where",
Value: "> " + where,
})
}
dat, err := Hook.SendWait(dwhook.Message{
Content: Conf.BaseURL + s.ApprovalURL(),
Embeds: []dwhook.Embed{{
Title: title,
URL: s.URI,
Description: Conf.BaseURL + s.ApprovalURL(),
Fields: fields,
Thumbnail: dwhook.EmbedThumbnail{
URL: thumb,
},
Footer: dwhook.EmbedFooter{
Text: "ID: " + s.ID.String() + " From: " + ifdef(s.Submitter, "anon"),
},
}},
})
if err != nil {
log.Println(string(dat))
return err
}
msg, err := dwhook.UnmarshalResponse(dat, nil)
if err != nil {
return err
}
return AddSubmissionWebhook(s.ID, msg.ID)
}()
if err != nil {
log.Println(err)
}
}
for _, x := range Conf.Submissions.NotifTopics {
err := func() error {
req, err := http.NewRequest("POST", "https://ntfy.sh/"+x,
strings.NewReader(title+"\nBy: "+ifdef(s.Submitter, "anon")+"\nName: "+name+"\nTags:\n"+s.Tags))
if err != nil {
return err
}
req.Header.Set("X-Title", "ACD Submission")
req.Header.Set("X-Priority", "low")
req.Header.Set("X-Tags", "inbox_tray")
req.Header.Set("X-Actions", fmt.Sprintf("http, Approve, %[1]v&action=approve; http, Delete, %[1]v&action=delete",
Conf.BaseURL+s.ApprovalURL(),
))
req.Header.Set("X-Click", Conf.BaseURL+s.ApprovalURL())
req.Header.Set("user-agent", "ACD Submissions")
req.Header.Set("from", Conf.HostEmail)
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}()
if err != nil {
log.Println(err)
}
}
}
func logAction(s Submission, what uint, remote string) {
title := s.FmtName()
hash := md5.Sum(append([]byte(remote),
63, 214, 229, 9, 121, 125, 12, 234, 107, 74, 89, 214, 54, 131, 96, 36,
243, 167, 64, 145, 162, 176, 44, 225, 95, 84, 91, 16, 67, 247, 132, 41))
who := base64.RawStdEncoding.EncodeToString(hash[:])
// md5 isn't secure, but being the input has to be a valid IP i don't think there is much risk
// also add 32 bytes of pepper to keep the rainbow tables out
// i sure love vague magic numbers with no constants to reference from
message := ""
switch what {
case 1:
message = "approved"
case 2:
message = "unapproved"
case 3:
message = "deleted"
}
if Hook != nil {
err := func() error {
fields := []dwhook.EmbedField{{
Name: "Maker",
Value: "> [" + title + "](" + s.URI + ")",
}, {
Name: "Tags",
Value: "> " + s.Tags,
}}
dat, err := AuditHook.Send(dwhook.Message{
Embeds: []dwhook.Embed{{
Title: message + ": " + s.ID.String(),
URL: Conf.BaseURL + "/pending?id=" + s.ID.String(),
Fields: fields,
Footer: dwhook.EmbedFooter{
Text: "Actor: " + who,
},
}},
})
if err != nil {
log.Println(string(dat))
return err
}
return nil
}()
if err != nil {
log.Println(err)
}
}
// i have decided my phone buzzing every time somebody does something would be too annoying
// edit: i have still deemed it would but too annoying, i have just decided to put it in its own config
for _, x := range Conf.Audit.NotifTopics {
err := func() error {
req, err := http.NewRequest("POST", "https://ntfy.sh/"+x,
strings.NewReader(message+" "+s.ID.String()+"\nBy: "+who+"\nMaker: "+title+"\nTags: "+s.Tags))
if err != nil {
return err
}
req.Header.Set("X-Title", "ACD "+message)
req.Header.Set("X-Priority", "min")
req.Header.Set("X-Tags", "inbox_tray")
req.Header.Set("X-Click", Conf.BaseURL+"/pending?id="+s.ID.String())
req.Header.Set("user-agent", "ACD Submissions")
req.Header.Set("from", Conf.HostEmail)
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}()
if err != nil {
log.Println(err)
}
}
}
func SplitAndDeduplicate(s string) []string {
tags := splitTags(s)
good := make([]string, len(tags))
ptr := 0
otherloop:
for xi, x := range tags {
for yi, y := range tags {
if strings.EqualFold(x, y) && yi > xi {
continue otherloop
}
}
good[ptr] = x
ptr++
}
good = good[:ptr]
return good
}