3000/cmds/shake.go
2023-11-19 19:50:31 -06:00

140 lines
3.2 KiB
Go

package cmds
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"math"
"math/rand"
"os"
"os/exec"
"path/filepath"
"strconv"
"sync"
"github.com/bwmarrin/discordgo"
)
var ShakeCommand = &discordgo.ApplicationCommand{
Name: "shake",
Description: "Generates a shaking animated gif",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionAttachment,
Name: "image",
Description: "Image to shake",
Required: false,
},
{
Type: discordgo.ApplicationCommandOptionUser,
Name: "user",
Description: "User whose avatar to shake",
Required: false,
},
{
Type: discordgo.ApplicationCommandOptionNumber,
Name: "speed",
Description: "speed multiplier (no more than 2)",
Required: false,
},
},
}
func ShakeCommandHandler(s *discordgo.Session, i *discordgo.InteractionCreate) error {
options := i.ApplicationCommandData().Options
optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options))
for _, opt := range options {
optionMap[opt.Name] = opt
}
var frameDelay float64
if option, ok := optionMap["speed"]; ok {
frameDelay = math.Floor(math.Max(5/math.Min(option.FloatValue(), 2), 1))
} else {
frameDelay = 5
}
imageReader, err := getImage(s, i)
if err != nil {
return err
}
defer imageReader.Close()
ogDir, err := os.MkdirTemp("", "b-i3-og-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(ogDir)
newImgFilePath := filepath.Join(ogDir, "new-img")
newImgFile, err := os.Create(newImgFilePath)
if err != nil {
return err
}
defer newImgFile.Close()
io.Copy(newImgFile, imageReader)
dir, err := os.MkdirTemp("", "b-i3-frames-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(dir)
frameCount := 15
var wg sync.WaitGroup
shakeRand := rand.New(rand.NewSource(724))
for i := 0; i < frameCount; i++ {
wg.Add(1)
i := i
go func(coords [2]int) {
defer wg.Done()
newFrame := filepath.Join(dir, fmt.Sprintf("%v.png", strconv.Itoa(i)))
magickCmd := exec.Command("magick", newImgFilePath, "-page", fmt.Sprintf("%+d%+d", coords[0], coords[1]), "-background", "none", "-flatten", newFrame)
magickCmd.Stdin = imageReader
magickOutput, err := magickCmd.CombinedOutput()
if err != nil {
panic(errors.New(fmt.Sprintf("magick: %s\n%s", err.Error(), string(magickOutput))))
}
}([2]int{shakeRand.Intn(21) - 10, shakeRand.Intn(21) - 10})
}
wg.Wait()
magickCmd := exec.Command("magick", "-delay", fmt.Sprintf("%f", frameDelay), "-dispose", "Background", "-loop", "0", filepath.Join(dir, "*.png"), "gif:fd:1")
magickCmd.Stdin = imageReader
magickOutput, err := magickCmd.CombinedOutput()
if err != nil {
fmt.Println(magickOutput)
return errors.New(fmt.Sprintf("magick: %s\n%s", err.Error(), string(magickOutput)))
}
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Files: []*discordgo.File{
{
Name: "shake.gif",
Reader: bytes.NewReader(magickOutput),
ContentType: "image/gif",
},
},
},
})
if err != nil {
return err
}
return nil
}