initial commit

This commit is contained in:
Cadey Ratio 2021-02-27 15:44:57 -05:00
commit dc5d5b0653
7 changed files with 217 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
var/*

60
discordwebhook.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
"bytes"
"encoding/json"
"net/http"
)
// Webhook is the parent structure fired off to Discord.
type Webhook struct {
Embeds []Embed `json:"embeds,omitifempty"`
}
// EmbedField is an individual field being embedded in a message.
type EmbedField struct {
Name string `json:"name"`
Value string `json:"value"`
Inline bool `json:"inline"`
}
// EmbedFooter is the message footer.
type EmbedFooter struct {
Text string `json:"text"`
IconURL string `json:"icon_url"`
}
// Embed is a set of embedded fields and a footer.
type Embed struct {
Title string `json:"title"`
Description string `json:"description"`
URL string `json:"url"`
Fields []EmbedField `json:"fields"`
Footer EmbedFooter `json:"footer"`
}
// Send returns a request for a Discord webhook.
func Send(whurl string, w Webhook) *http.Request {
data, err := json.Marshal(&w)
if err != nil {
panic(err)
}
req, err := http.NewRequest(http.MethodPost, whurl, bytes.NewBuffer(data))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
return req
}
// Validate validates the response from Discord.
func Validate(resp *http.Response) error {
if resp.StatusCode != http.StatusOK {
return NewError(http.StatusOK, resp)
}
return nil
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module tulpa.dev/cadey/snoo2nebby
go 1.16
require github.com/turnage/graw v0.0.0-20201204201853-a177df1b5c91 // indirect

23
go.sum Normal file
View File

@ -0,0 +1,23 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/turnage/graw v0.0.0-20201204201853-a177df1b5c91 h1:vYoyWnsUWuvaLGe6369mItyePB2EVFRjrvkev7xFuGQ=
github.com/turnage/graw v0.0.0-20201204201853-a177df1b5c91/go.mod h1:aAkq4I/q1izZSSwHvzhDn9NA+eGxgTSuibwP3MZRlQY=
github.com/turnage/redditproto v0.0.0-20151223012412-afedf1b6eddb h1:qR56NGRvs2hTUbkn6QF8bEJzxPIoMw3Np3UigBeJO5A=
github.com/turnage/redditproto v0.0.0-20151223012412-afedf1b6eddb/go.mod h1:GyqJdEoZSNoxKDb7Z2Lu/bX63jtFukwpaTP9ZIS5Ei0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

83
main.go Normal file
View File

@ -0,0 +1,83 @@
package main
import (
"bytes"
"flag"
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/turnage/graw/reddit"
"github.com/turnage/graw/streams"
)
// UA is the user agent for Reddit
const UA = `NixOS:tulpa.dev/cadey/snoo2nebby:v0.1.0 (by /u/shadowh511)`
var (
webhookFile = flag.String("webhook-file", "./var/webhook.txt", "where the Discord webhook file is located")
subreddit = flag.String("subreddit", "tulpas", "the subreddit to monitor")
pokeFreq = flag.Duration("poke-frequency", 5*time.Minute, "how often the bot should poke the feed")
)
func main() {
flag.Parse()
script, err := reddit.NewScript(UA, *pokeFreq)
if err != nil {
log.Fatal(err)
}
whSlc, err := os.ReadFile(*webhookFile)
if err != nil {
log.Fatal(err)
}
whURL := string(bytes.TrimSpace(whSlc))
kill := make(chan bool)
errs := make(chan error)
stream, err := streams.Subreddits(script, kill, errs, *subreddit)
if err != nil {
log.Fatal(err)
}
go func(errs chan error) {
for err := range errs {
log.Printf("%v", err)
}
}(errs)
log.Printf("listening for new posts on /r/%s", *subreddit)
for post := range stream {
log.Printf("got new post: by /u/%s: %q %s", post.Author, post.URL, post.Title)
wh := Webhook{
Embeds: []Embed{
{
Title: post.Title,
URL: post.URL,
Description: post.SelfText,
Footer: EmbedFooter{
Text: "by /u/" + post.Author,
},
},
},
}
req := Send(whURL, wh)
req.Header.Set("User-Agent", UA)
resp, err := http.DefaultClient.Do(req)
if err != nil {
errs <- fmt.Errorf("can't send webhook: %w", err)
continue
}
err = Validate(resp)
if err != nil {
errs <- fmt.Errorf("can't validate response: %w", err)
continue
}
}
}

0
var/.gitkeep Normal file
View File

45
web.go Normal file
View File

@ -0,0 +1,45 @@
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
// NewError creates an Error based on an expected HTTP status code vs data populated
// from an HTTP response.
//
// This consumes the body of the HTTP response.
func NewError(wantStatusCode int, resp *http.Response) error {
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
resp.Body.Close()
loc, err := resp.Location()
if err != nil {
loc = resp.Request.URL
}
return &Error{
WantStatus: wantStatusCode,
GotStatus: resp.StatusCode,
URL: loc,
Method: resp.Request.Method,
ResponseBody: string(data),
}
}
// Error is a web response error. Use this when API calls don't work out like you wanted them to.
type Error struct {
WantStatus, GotStatus int
URL *url.URL
Method string
ResponseBody string
}
func (e Error) Error() string {
return fmt.Sprintf("%s %s: wanted status code %d, got: %d: %v", e.Method, e.URL, e.WantStatus, e.GotStatus, e.ResponseBody)
}