move bot to cmd/
This commit is contained in:
parent
931404890e
commit
07651ae4a7
|
@ -1,2 +1 @@
|
||||||
.env
|
.env
|
||||||
vyvanse
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
vyvanse
|
|
@ -0,0 +1,27 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/justinian/dice"
|
||||||
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func roll(ctx context.Context, s *discordgo.Session, m *discordgo.Message, parv []string) error {
|
||||||
|
sp, ctx := opentracing.StartSpanFromContext(ctx, "dice")
|
||||||
|
defer sp.Finish()
|
||||||
|
|
||||||
|
if len(parv) != 2 {
|
||||||
|
return errors.New("not enough parameters (expected 1)")
|
||||||
|
}
|
||||||
|
|
||||||
|
text, _, err := dice.Roll(parv[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ChannelMessageSend(m.ChannelID, text.String())
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/google/gops/agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if err := agent.Listen(nil); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Xe/ln"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hipster(ctx context.Context, s *discordgo.Session, m *discordgo.Message, parv []string) error {
|
||||||
|
sp, ctx := opentracing.StartSpanFromContext(ctx, "hipster")
|
||||||
|
defer sp.Finish()
|
||||||
|
|
||||||
|
msg, err := getHipsterText(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(ctx, err, ln.F{"action": "get hipster text"})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ChannelMessageSend(m.ChannelID, msg)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHipsterText(ctx context.Context) (string, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://hipsterjesus.com/api/?type=hipster-centric&html=false¶s=1", nil)
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
textStruct := &struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
json.Unmarshal(body, textStruct)
|
||||||
|
|
||||||
|
text := strings.Split(textStruct.Text, ". ")[0]
|
||||||
|
|
||||||
|
return text, nil
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.xeserv.us/xena/vyvanse/bot"
|
||||||
|
"github.com/Xe/ln"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
_ "github.com/joho/godotenv/autoload"
|
||||||
|
"github.com/namsral/flag"
|
||||||
|
xkcd "github.com/nishanths/go-xkcd"
|
||||||
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
otlog "github.com/opentracing/opentracing-go/log"
|
||||||
|
zipkin "github.com/openzipkin/zipkin-go-opentracing"
|
||||||
|
"github.com/robfig/cron"
|
||||||
|
)
|
||||||
|
|
||||||
|
var lastXKCD int
|
||||||
|
|
||||||
|
var (
|
||||||
|
pesterChannel = flag.String("upload-channel", "", "Discord channel ID to upload and announce new XKCD items to")
|
||||||
|
token = flag.String("token", "", "discord bot token")
|
||||||
|
zipkinURL = flag.String("zipkin-url", "", "URL for Zipkin traces")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
xk := xkcd.NewClient()
|
||||||
|
dg, err := discordgo.New("Bot " + *token)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := cron.New()
|
||||||
|
|
||||||
|
comic, err := xk.Latest()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
lastXKCD = comic.Number
|
||||||
|
|
||||||
|
c.AddFunc("@daily", func() {
|
||||||
|
sp, ctx := opentracing.StartSpanFromContext(context.Background(), "daily.xkcd.fetch")
|
||||||
|
|
||||||
|
comic, err := xk.Latest()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if comic.Number > lastXKCD {
|
||||||
|
sp.LogFields(otlog.String("image.url", comic.ImageURL), otlog.String("target", *pesterChannel))
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, comic.ImageURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(ctx, err, ln.F{"action": "make request"})
|
||||||
|
sp.LogFields(otlog.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(ctx, err, ln.F{"action": "http get", "url": comic.ImageURL})
|
||||||
|
sp.LogFields(otlog.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fname := fmt.Sprintf("%d - %s.png", comic.Number, comic.Title)
|
||||||
|
msg := fmt.Sprintf("New XKCD comic uploaded: %d - %s\n\n*%s*", comic.Number, comic.Title, comic.Alt)
|
||||||
|
_, err = dg.ChannelFileSendWithMessage(*pesterChannel, msg, fname, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(ctx, err, ln.F{"action": "send xkcd upload", "to": *pesterChannel})
|
||||||
|
sp.LogFields(otlog.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastXKCD = comic.Number
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
c.Start()
|
||||||
|
|
||||||
|
if *zipkinURL != "" {
|
||||||
|
collector, err := zipkin.NewHTTPCollector(*zipkinURL)
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(context.Background(), err)
|
||||||
|
}
|
||||||
|
tracer, err := zipkin.NewTracer(
|
||||||
|
zipkin.NewRecorder(collector, false, "vyvanse:5000", "vyvanse"))
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(context.Background(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opentracing.SetGlobalTracer(tracer)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := bot.NewCommandSet()
|
||||||
|
cs.Prefix = ">"
|
||||||
|
|
||||||
|
cs.AddCmd("hipster", "generates hipster-sounding text", bot.NoPermissions, hipster)
|
||||||
|
cs.AddCmd("printerfact", "facts about printers", bot.NoPermissions, printerFact)
|
||||||
|
cs.AddCmd("dice", "roll the dice", bot.NoPermissions, roll)
|
||||||
|
cs.AddCmd("splattus", "splatoon 2 map rotation status", bot.NoPermissions, spla2nMaps)
|
||||||
|
|
||||||
|
dg.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
err := cs.Run(s, m.Message)
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(context.Background(), err, ln.F{"action": "run commandSet on message"})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Open the websocket and begin listening.
|
||||||
|
err = dg.Open()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error opening connection,", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Bot is now running. Press CTRL-C to exit.")
|
||||||
|
// Simple way to keep program running until CTRL-C is pressed.
|
||||||
|
<-make(chan struct{})
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printerFact(ctx context.Context, s *discordgo.Session, m *discordgo.Message, parv []string) error {
|
||||||
|
sp, ctx := opentracing.StartSpanFromContext(ctx, "printer.fact")
|
||||||
|
defer sp.Finish()
|
||||||
|
|
||||||
|
fact, err := getPrinterFact()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ChannelMessageSend(m.ChannelID, fact)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrinterFact() (string, error) {
|
||||||
|
resp, err := http.Get("https://xena.stdlib.com/printerfacts")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
factStruct := &struct {
|
||||||
|
Facts []string `json:"facts"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
json.Unmarshal(body, factStruct)
|
||||||
|
|
||||||
|
text := fmt.Sprintf("%s", factStruct.Facts[0])
|
||||||
|
|
||||||
|
return text, nil
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
otlog "github.com/opentracing/opentracing-go/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func spla2nMaps(ctx context.Context, s *discordgo.Session, msg *discordgo.Message, parv []string) error {
|
||||||
|
sp, ctx := opentracing.StartSpanFromContext(ctx, "spla2nMaps")
|
||||||
|
defer sp.Finish()
|
||||||
|
|
||||||
|
resp, err := http.Get("https://splatoon.ink/schedule2")
|
||||||
|
if err != nil {
|
||||||
|
sp.LogFields(otlog.Error(err), otlog.String("step", "http get"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
st := &splattus{}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
sp.LogFields(otlog.Error(err), otlog.String("step", "http response read"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
json.Unmarshal(body, st)
|
||||||
|
|
||||||
|
var modeInfo []string
|
||||||
|
|
||||||
|
for _, mode := range st.Modes.Regular {
|
||||||
|
if mode.Active() {
|
||||||
|
modeInfo = append(modeInfo, mode.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mode := range st.Modes.Gachi {
|
||||||
|
if mode.Active() {
|
||||||
|
modeInfo = append(modeInfo, mode.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mode := range st.Modes.League {
|
||||||
|
if mode.Active() {
|
||||||
|
modeInfo = append(modeInfo, mode.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text := strings.Join(modeInfo, "\n")
|
||||||
|
|
||||||
|
_, err = s.ChannelMessageSend(msg.ChannelID, text)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type splatoonMode struct {
|
||||||
|
StartTime int64 `json:"startTime"`
|
||||||
|
EndTime int64 `json:"endTime"`
|
||||||
|
Maps []string `json:"maps"`
|
||||||
|
Rule splatoonRule `json:"rule"`
|
||||||
|
Mode splatoonGameMode `json:"mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm splatoonMode) Active() bool {
|
||||||
|
beg := time.Unix(sm.StartTime, 0)
|
||||||
|
end := time.Unix(sm.EndTime, 0)
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
return now.After(beg) && now.Before(end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm splatoonMode) String() string {
|
||||||
|
maps := strings.Join(sm.Maps, ", ")
|
||||||
|
end := time.Unix(sm.EndTime, 0)
|
||||||
|
now := time.Now()
|
||||||
|
diff := end.Sub(now)
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s:\nRotation ends at %s (in %s)\nMaps: %s\nRule: %s\n", sm.Mode, end.Format(time.RFC3339), diff, maps, sm.Rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
type splatoonGameMode struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sgm splatoonGameMode) String() string {
|
||||||
|
return sgm.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type splatoonRule struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
MultilineName string `json:"multiline_name"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr splatoonRule) String() string {
|
||||||
|
return sr.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type splattus struct {
|
||||||
|
UpdateTime int64 `json:"updateTime"`
|
||||||
|
Modes struct {
|
||||||
|
League []splatoonMode `json:"league"`
|
||||||
|
Regular []splatoonMode `json:"regular"`
|
||||||
|
Gachi []splatoonMode `json:"gachi"`
|
||||||
|
} `json:"modes"`
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
version: "3"
|
version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
# Persistence layers and dev tools
|
||||||
|
|
||||||
|
# http://127.0.0.1:9411
|
||||||
zipkin:
|
zipkin:
|
||||||
image: openzipkin/zipkin
|
image: openzipkin/zipkin
|
||||||
environment:
|
environment:
|
||||||
|
@ -8,8 +11,26 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "9411:9411"
|
- "9411:9411"
|
||||||
|
|
||||||
|
# message queue
|
||||||
|
mq:
|
||||||
|
image: drone/mq
|
||||||
|
|
||||||
|
# database
|
||||||
|
rqlite:
|
||||||
|
restart: always
|
||||||
|
image: rqlite/rqlite:4.0.2
|
||||||
|
volumes:
|
||||||
|
- rqlite:/rqlite/file
|
||||||
|
command: -on-disk -http-adv-addr rqlite:4001
|
||||||
|
|
||||||
|
# the bot and event sourcing ingress
|
||||||
vyvanse:
|
vyvanse:
|
||||||
image: xena/vyvanse
|
image: xena/vyvanse
|
||||||
env_file: ./.env
|
env_file: ./.env
|
||||||
depends_on:
|
depends_on:
|
||||||
- zipkin
|
- zipkin
|
||||||
|
- mq
|
||||||
|
- rqlite
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
rqlite:
|
||||||
|
|
Loading…
Reference in New Issue