234 lines
5.8 KiB
Go
234 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"git.xeserv.us/xena/gorqlite"
|
|
"git.xeserv.us/xena/vyvanse/bot"
|
|
"git.xeserv.us/xena/vyvanse/internal/dao"
|
|
|
|
"github.com/Xe/ln"
|
|
"github.com/bwmarrin/discordgo"
|
|
_ "github.com/joho/godotenv/autoload"
|
|
"github.com/namsral/flag"
|
|
nats "github.com/nats-io/go-nats"
|
|
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")
|
|
databaseURL = flag.String("database-url", "http://", "URL for database (rqlite)")
|
|
natsURL = flag.String("nats-url", "nats://localhost:4222", "URL for Nats message queue")
|
|
)
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
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)
|
|
}
|
|
|
|
xk := xkcd.NewClient()
|
|
dg, err := discordgo.New("Bot " + *token)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
db, err := gorqlite.Open(*databaseURL)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
us := dao.NewUsers(db)
|
|
|
|
type migrator interface {
|
|
Migrate(ctx context.Context) error
|
|
}
|
|
mgs := []migrator{us}
|
|
|
|
sp, ctx := opentracing.StartSpanFromContext(context.Background(), "migrations")
|
|
for _, mg := range mgs {
|
|
err := mg.Migrate(ctx)
|
|
if err != nil {
|
|
sp.Finish()
|
|
ln.FatalErr(ctx, err)
|
|
}
|
|
}
|
|
sp.Finish()
|
|
|
|
ctx = context.Background()
|
|
|
|
mq, err := nats.Connect(*natsURL)
|
|
if err != nil {
|
|
ln.FatalErr(ctx, 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()
|
|
|
|
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)
|
|
cs.AddCmd("top10", "shows the top 10 chatters on this server", bot.NoPermissions, top10(us))
|
|
|
|
dg.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
sp, ctx := opentracing.StartSpanFromContext(context.Background(), "message.create.post.stomp")
|
|
defer sp.Finish()
|
|
|
|
f := ln.F{
|
|
"channel_id": m.ChannelID,
|
|
"message_id": m.ID,
|
|
"message_author": m.Author.ID,
|
|
"message_author_name": m.Author.Username,
|
|
"message_author_is_bot": m.Author.Bot,
|
|
}
|
|
|
|
data, err := json.Marshal(m)
|
|
if err != nil {
|
|
ln.Error(ctx, err, f)
|
|
return
|
|
}
|
|
|
|
err = mq.Publish("/message/create", data)
|
|
if err != nil {
|
|
ln.Error(ctx, err, f, ln.F{"action": "send new message to nats"})
|
|
return
|
|
}
|
|
|
|
ln.Log(ctx, f, ln.F{"action": "message_create"})
|
|
})
|
|
|
|
dg.AddHandler(func(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
|
|
sp, ctx := opentracing.StartSpanFromContext(context.Background(), "member.add.post.stomp")
|
|
defer sp.Finish()
|
|
|
|
f := ln.F{
|
|
"guild_id": m.GuildID,
|
|
"user_id": m.User.ID,
|
|
"user_name": m.User.Username,
|
|
}
|
|
|
|
data, err := json.Marshal(m.Member)
|
|
if err != nil {
|
|
ln.Error(ctx, err, f, ln.F{"action": "prepare member add to queue"})
|
|
}
|
|
|
|
err = mq.Publish("/member/add", data)
|
|
if err != nil {
|
|
ln.Error(ctx, err, f, ln.F{"action": "send added member to queue"})
|
|
}
|
|
|
|
ln.Log(ctx, f, ln.F{"action": "member_add"})
|
|
})
|
|
|
|
dg.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
if m.Author.ID == s.State.User.ID {
|
|
return
|
|
}
|
|
|
|
sp, ctx := opentracing.StartSpanFromContext(context.Background(), "discordgo.message.create")
|
|
defer sp.Finish()
|
|
|
|
err := cs.Run(ctx, s, m.Message)
|
|
if err != nil {
|
|
ln.Error(ctx, err, ln.F{"action": "run commandSet on message"})
|
|
}
|
|
|
|
_, err = us.Insert(ctx, m.Author.ID)
|
|
if err != nil && !strings.Contains(err.Error(), "UNIQUE constraint failed") {
|
|
ln.Error(ctx, err, ln.F{"action": "insert user into database"})
|
|
}
|
|
|
|
err = us.IncScore(ctx, m.Author.ID)
|
|
if err != nil {
|
|
ln.Error(ctx, err, ln.F{"action": "increment user score"})
|
|
}
|
|
})
|
|
|
|
// Open the websocket and begin listening.
|
|
err = dg.Open()
|
|
if err != nil {
|
|
fmt.Println("error opening connection,", err)
|
|
return
|
|
}
|
|
|
|
ln.Log(ctx, ln.F{"action": "bot is running"})
|
|
|
|
for {
|
|
select {}
|
|
}
|
|
}
|