catbase/plugins/tappd/tappd.go

208 lines
4.6 KiB
Go

package tappd
import (
"bytes"
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/config"
"github.com/velour/catbase/connectors/discord"
"image"
"os"
"path"
"regexp"
"time"
)
type Tappd struct {
b bot.Bot
c *config.Config
imageMap map[string]imageInfo
}
type imageInfo struct {
ID string
SrcURL string
BotURL string
Img image.Image
Repr []byte
W int
H int
MimeType string
FileName string
}
func New(b bot.Bot) *Tappd {
t := &Tappd{
b: b,
c: b.Config(),
imageMap: make(map[string]imageInfo),
}
t.register()
t.registerWeb()
t.mkDB()
return t
}
func (p *Tappd) mkDB() error {
db := p.b.DB()
tx, err := db.Beginx()
if err != nil {
tx.Rollback()
return err
}
_, err = tx.Exec(`create table if not exists tappd (
id string primary key,
who string,
channel string,
message string,
ts datetime
);`)
if err != nil {
tx.Rollback()
return err
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func (p *Tappd) log(id, who, channel, message string) error {
db := p.b.DB()
tx, err := db.Beginx()
if err != nil {
tx.Rollback()
return err
}
_, err = tx.Exec(`insert into tappd (id, who, channel, message, ts) values (?, ?, ?, ? ,?)`,
id, who, channel, message, time.Now())
if err != nil {
tx.Rollback()
return err
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func (p *Tappd) registerDiscord(d *discord.Discord) {
cmd := discordgo.ApplicationCommand{
Name: "tap",
Description: "tappd a beer in",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionAttachment,
Name: "image",
Description: "Picture that beer, but on Discord",
Required: true,
},
{
Type: discordgo.ApplicationCommandOptionString,
Name: "comment",
Description: "Comment on that beer",
Required: true,
},
},
}
if err := d.RegisterSlashCmd(cmd, p.tap); err != nil {
log.Error().Err(err).Msgf("could not register")
}
}
func (p *Tappd) tap(s *discordgo.Session, i *discordgo.InteractionCreate) {
who := i.Interaction.Member.Nick
channel := i.Interaction.ChannelID
shortMsg := i.ApplicationCommandData().Options[1].StringValue()
longMsg := fmt.Sprintf("%s checked in: %s",
i.Interaction.Member.Nick, shortMsg)
attachID := i.ApplicationCommandData().Options[0].Value.(string)
attach := i.ApplicationCommandData().Resolved.Attachments[attachID]
spec := defaultSpec()
spec.text = shortMsg
info, err := p.getAndOverlay(attachID, attach.URL, []textSpec{spec})
if err != nil {
log.Error().Err(err).Msgf("error with interaction")
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "Error getting the image",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
log.Error().Err(err).Msgf("error with interaction")
}
return
}
embeds := []*discordgo.MessageEmbed{{
Description: longMsg,
Image: &discordgo.MessageEmbedImage{
URL: info.BotURL,
Width: info.W,
Height: info.H,
},
}}
files := []*discordgo.File{{
Name: info.FileName,
ContentType: info.MimeType,
Reader: bytes.NewBuffer(info.Repr),
}}
content := info.BotURL
// Yes, the configs are all stringly typed. Get over it.
useEmbed := p.c.GetBool("tappd.embed", false)
if useEmbed {
files = nil
} else {
embeds = nil
content = ""
}
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Embeds: embeds,
Files: files,
Content: content,
},
})
if err != nil {
log.Error().Err(err).Msgf("error with interaction")
return
}
err = p.log(info.ID, who, channel, shortMsg)
if err != nil {
log.Error().Err(err).Msgf("error recording tap")
}
imgPath := p.c.Get("tappd.imagepath", "tappdimg")
err = os.MkdirAll(imgPath, 0775)
if err != nil {
log.Error().Err(err).Msgf("error creating directory")
return
}
err = os.WriteFile(path.Join(imgPath, info.FileName), info.Repr, 0664)
if err != nil {
log.Error().Err(err).Msgf("error writing file")
}
}
func (p *Tappd) register() {
ht := bot.HandlerTable{
{
Kind: bot.Startup, IsCmd: false,
Regex: regexp.MustCompile(`.*`),
Handler: func(r bot.Request) bool {
switch conn := r.Conn.(type) {
case *discord.Discord:
p.registerDiscord(conn)
}
return false
},
},
}
p.b.RegisterTable(p, ht)
}