282 lines
6.5 KiB
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
|
|
}
|