Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slash commands #65

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[build]
cmd = "go build -o ./borik ."
bin = "./borik run"
include_ext = ["go", "env"]
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/rs/zerolog v1.32.0
github.com/spf13/cobra v1.8.0
gopkg.in/gographics/imagick.v3 v3.6.0
pkg.nit.so/switchboard v0.0.0-20240930133908-cf045dc997ba
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/gographics/imagick.v3 v3.6.0 h1:MzgZPeYPD0fh1SZl2PlM6Z3tfMKZ/XCHdnhudf4oum0=
gopkg.in/gographics/imagick.v3 v3.6.0/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
pkg.nit.so/switchboard v0.0.0-20240930133908-cf045dc997ba h1:OiDsks1Kf2VMOaVDg01IRFOs2GUJx3jKoGnmgbUfK+k=
pkg.nit.so/switchboard v0.0.0-20240930133908-cf045dc997ba/go.mod h1:6w0eM5WM34YQ+uqM+Sj9o2hO8Qrs+p2nlTDdAP/DkTQ=
247 changes: 213 additions & 34 deletions pkg/bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ package bot

import (
"fmt"
"slices"

"github.com/bwmarrin/discordgo"
"github.com/nint8835/parsley"
"github.com/rs/zerolog/log"
"pkg.nit.so/switchboard"

configPkg "github.com/fogo-sh/borik/pkg/config"
)

// Bot represents an individual instance of Borik
type Bot struct {
session *discordgo.Session
config *configPkg.Config
parser *parsley.Parser
quitChan chan struct{}
session *discordgo.Session
config *configPkg.Config
textParser *parsley.Parser
slashParser *switchboard.Switchboard
quitChan chan struct{}
}

func (b *Bot) Start() error {
Expand All @@ -41,6 +44,172 @@ func (b *Bot) Stop() {
// Instance is the current instance of Borik
var Instance *Bot

type Command struct {
name string
description string
textHandler interface{}
slashHandler interface{}
}

var commands = []Command{
{
name: "magik",
description: "Magikify an image.",
textHandler: MakeImageOpTextCommand(Magik),
slashHandler: MakeImageOpSlashCommand(Magik),
},
{
name: "lagik",
description: "Lagikify an image.",
textHandler: MakeImageOpTextCommand(Lagik),
slashHandler: MakeImageOpSlashCommand(Lagik),
},
{
name: "gmagik",
description: "Repeatedly magikify an image.",
textHandler: MakeImageOpTextCommand(Gmagik),
slashHandler: MakeImageOpSlashCommand(Gmagik),
},
{
name: "arcweld",
description: "Arc-weld an image.",
textHandler: MakeImageOpTextCommand(Arcweld),
slashHandler: MakeImageOpSlashCommand(Arcweld),
},
{
name: "malt",
description: "Malt an image.",
textHandler: MakeImageOpTextCommand(Malt),
slashHandler: MakeImageOpSlashCommand(Malt),
},
{
name: "help",
description: "Get help for available commands.",
textHandler: HelpCommand,
slashHandler: nil,
},
{
name: "deepfry",
description: "Deep-fry an image.",
textHandler: MakeImageOpTextCommand(Deepfry),
slashHandler: MakeImageOpSlashCommand(Deepfry),
},
{
name: "divine",
description: "Sever the divine light.",
textHandler: MakeImageOpTextCommand(Divine),
slashHandler: MakeImageOpSlashCommand(Divine),
},
{
name: "waaw",
description: "Mirror the right half of an image.",
textHandler: MakeImageOpTextCommand(Waaw),
slashHandler: MakeImageOpSlashCommand(Waaw),
},
{
name: "haah",
description: "Mirror the left half of an image.",
textHandler: MakeImageOpTextCommand(Haah),
slashHandler: MakeImageOpSlashCommand(Haah),
},
{
name: "woow",
description: "Mirror the top half of an image.",
textHandler: MakeImageOpTextCommand(Woow),
slashHandler: MakeImageOpSlashCommand(Woow),
},
{
name: "hooh",
description: "Mirror the bottom half of an image.",
textHandler: MakeImageOpTextCommand(Hooh),
slashHandler: MakeImageOpSlashCommand(Hooh),
},
{
name: "invert",
description: "Invert the colours of an image.",
textHandler: MakeImageOpTextCommand(Invert),
slashHandler: MakeImageOpSlashCommand(Invert),
},
{
name: "otsu",
description: "Apply a threshold to an image using Otsu's method.",
textHandler: MakeImageOpTextCommand(Otsu),
slashHandler: MakeImageOpSlashCommand(Otsu),
},
{
name: "rotate",
description: "Rotate an image.",
textHandler: MakeImageOpTextCommand(Rotate),
slashHandler: MakeImageOpSlashCommand(Rotate),
},
{
name: "avatar",
description: "Fetch the avatar for a user.",
textHandler: Avatar,
slashHandler: nil,
},
{
name: "sticker",
description: "Fetch a sticker as an image.",
textHandler: Sticker,
slashHandler: nil,
},
{
name: "emoji",
description: "Fetch an emoji as an image.",
textHandler: Emoji,
slashHandler: nil,
},
{
name: "resize",
description: "Resize an image.",
textHandler: MakeImageOpTextCommand(Resize),
slashHandler: MakeImageOpSlashCommand(Resize),
},
{
name: "huecycle",
description: "Create a GIF cycling the hue of an image.",
textHandler: MakeImageOpTextCommand(HueCycle),
slashHandler: MakeImageOpSlashCommand(HueCycle),
},
{
name: "modulate",
description: "Modify the brightness, saturation, and hue of an image.",
textHandler: MakeImageOpTextCommand(Modulate),
slashHandler: MakeImageOpSlashCommand(Modulate),
},
{
name: "presidentsframe",
description: "Apply the President's Frame to an image",
textHandler: MakeImageOpTextCommand(PresidentsFrame),
slashHandler: MakeImageOpSlashCommand(PresidentsFrame),
},
}

var enabledCommands = []string{
"magik",
"lagik",
"gmagik",
"arcweld",
"malt",
"deepfry",
"divine",
"waaw",
"haah",
"woow",
"hooh",
"invert",
"otsu",
"rotate",
"avatar",
"sticker",
"emoji",
"resize",
"huecycle",
"modulate",
"presidentsframe",
}

// New constructs a new instance of Borik.
func New() (*Bot, error) {
config := configPkg.Instance
Expand All @@ -53,44 +222,54 @@ func New() (*Bot, error) {
session.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuildMessages)
log.Debug().Msg("Discord session created")

log.Debug().Msg("Creating command parser")
parser := parsley.New(config.Prefix)
parser.RegisterHandler(session)
log.Debug().Msg("Parser created")
log.Debug().Msg("Creating text command parser")
textParser := parsley.New(config.Prefix)
textParser.RegisterHandler(session)
log.Debug().Msg("Text command parser created")

log.Debug().Msg("Creating slash command parser")
slashParser := &switchboard.Switchboard{}
session.AddHandler(slashParser.HandleInteractionCreate)
log.Debug().Msg("Slash command parser created")

log.Debug().Msg("Registering commands")
_ = parser.NewCommand("", "Magikify an image.", MakeImageOpCommand(Magik))
_ = parser.NewCommand("magik", "Magikify an image.", MakeImageOpCommand(Magik))
_ = parser.NewCommand("lagik", "Lagikify an image.", MakeImageOpCommand(Lagik))
_ = parser.NewCommand("gmagik", "Repeatedly magikify an image.", MakeImageOpCommand(Gmagik))
_ = parser.NewCommand("arcweld", "Arc-weld an image.", MakeImageOpCommand(Arcweld))
_ = parser.NewCommand("malt", "Malt an image.", MakeImageOpCommand(Malt))
_ = parser.NewCommand("help", "Get help for available commands.", HelpCommand)
_ = parser.NewCommand("deepfry", "Deep-fry an image.", MakeImageOpCommand(Deepfry))
_ = parser.NewCommand("divine", "Sever the divine light.", MakeImageOpCommand(Divine))
_ = parser.NewCommand("waaw", "Mirror the right half of an image.", MakeImageOpCommand(Waaw))
_ = parser.NewCommand("haah", "Mirror the left half of an image.", MakeImageOpCommand(Haah))
_ = parser.NewCommand("woow", "Mirror the top half of an image.", MakeImageOpCommand(Woow))
_ = parser.NewCommand("hooh", "Mirror the bottom half of an image.", MakeImageOpCommand(Hooh))
_ = parser.NewCommand("invert", "Invert the colours of an image.", MakeImageOpCommand(Invert))
_ = parser.NewCommand("otsu", "Apply a threshold to an image using Otsu's method.", MakeImageOpCommand(Otsu))
_ = parser.NewCommand("rotate", "Rotate an image.", MakeImageOpCommand(Rotate))
_ = parser.NewCommand("avatar", "Fetch the avatar for a user.", Avatar)
_ = parser.NewCommand("sticker", "Fetch a sticker as an image.", Sticker)
_ = parser.NewCommand("emoji", "Fetch an emoji as an image.", Emoji)
_ = parser.NewCommand("resize", "Resize an image.", MakeImageOpCommand(Resize))
_ = parser.NewCommand("huecycle", "Create a GIF cycling the hue of an image.", MakeImageOpCommand(HueCycle))
_ = parser.NewCommand("modulate", "Modify the brightness, saturation, and hue of an image.", MakeImageOpCommand(Modulate))
_ = parser.NewCommand("presidentsframe", "Apply the President's Frame to an image", MakeImageOpCommand(PresidentsFrame))
registerGraphicsFormatCommands(parser)
registerOverlayCommands(parser)

_ = textParser.NewCommand("", "Magikify an image.", MakeImageOpTextCommand(Magik))

for _, command := range commands {
if slices.Contains(enabledCommands, command.name) {
_ = textParser.NewCommand(
command.name,
command.description,
command.textHandler,
)

if command.slashHandler != nil {
_ = slashParser.AddCommand(&switchboard.Command{
Name: command.name,
Description: command.description,
Handler: command.slashHandler,
GuildID: "564979920074440724", // TODO: make this configurable
})
}
}
}

registerGraphicsFormatCommands(textParser)
registerOverlayCommands(textParser)

err = slashParser.SyncCommands(session, "411546362547666951") // TODO: make this configurable
if err != nil {
return nil, fmt.Errorf("error syncing commands: %w", err)
}

log.Debug().Msg("Commands registered")

Instance = &Bot{
session,
config,
parser,
textParser,
slashParser,
make(chan struct{}),
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/bot/graphics_formats.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (args graphicsFormatArgs) GetImageURL() string {
}

func MakeGraphicsFormatOpCommand(format graphicsFormat) func(*discordgo.MessageCreate, graphicsFormatArgs) {
return MakeImageOpCommand(func(wand *imagick.MagickWand, args graphicsFormatArgs) ([]*imagick.MagickWand, error) {
return MakeImageOpTextCommand(func(wand *imagick.MagickWand, args graphicsFormatArgs) ([]*imagick.MagickWand, error) {
return convertGraphicsFormat(wand, format, args.Dither)
})
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/bot/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ type HelpArgs struct {
func generateCommandList() string {
commandCodeBlock := "```"

for _, details := range Instance.parser.GetCommands() {
for _, details := range Instance.textParser.GetCommands() {
commandCodeBlock += fmt.Sprintf("%s%s: %s\n", Instance.config.Prefix, details.Name, details.Description)
}

return commandCodeBlock + "```"
}

func generateCommandHelp(command string) (*discordgo.MessageEmbed, error) {
commandDetails, err := Instance.parser.GetCommand(command)
commandDetails, err := Instance.textParser.GetCommand(command)
if err != nil {
return nil, fmt.Errorf("error getting command details: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/bot/resize.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
)

type ResizeArgs struct {
ImageURL string `default:"" description:"URL to the image to process. Leave blank to automatically attempt to find an image."`
Mode string `default:"percent" description:"Mode of resizing to do. Controls how the width and height arguments are handled. Must be percent or absolute."`
Width float64 `description:"Target width. In absolute mode this should be the target width in pixels. For percent mode, this should be a percentage represented as a whole number (for example, 150 == 150%)"`
Height float64 `description:"Target height. In absolute mode this should be the target height in pixels. For percent mode, this should be a percentage represented as a whole number (for example, 150 == 150%)"`
Width float64 `description:"Width in pixels (absolute) or percent (e.g. 150 = 150%)."`
Height float64 `description:"Height in pixels (absolute) or percent (e.g. 150 = 150%)."`
ImageURL string `default:"" description:"Image URL to process. Leave blank to auto-find."`
Mode string `default:"percent" description:"Resize mode (percent/absolute) for width/height values."`
}

func (args ResizeArgs) GetImageURL() string {
Expand Down
Loading