140 lines
3.2 KiB
Go
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
|
|
}
|