Error handling

Embed responses
This commit is contained in:
Michał Gdula 2025-02-28 21:14:08 +00:00
parent b93820e9d0
commit f3929f2665
6 changed files with 169 additions and 48 deletions

View file

@ -2,14 +2,18 @@ package app
import (
"fmt"
"log"
"os"
"os/signal"
"strings"
"syscall"
"github.com/Fluffy-Bean/lynxie/utils"
"github.com/bwmarrin/discordgo"
)
type Callback func(h *Handler, args []string) Error
type Config struct {
Prefix string
Token string
@ -18,17 +22,17 @@ type Config struct {
type App struct {
Config Config
Commands map[string]func(h *Handler, args []string)
Commands map[string]Callback
}
func NewApp(config Config) *App {
return &App{
Config: config,
Commands: make(map[string]func(h *Handler, args []string)),
Commands: make(map[string]Callback),
}
}
func (a *App) RegisterCommand(cmd string, f func(h *Handler, args []string)) {
func (a *App) RegisterCommand(cmd string, f Callback) {
a.Commands[cmd] = f
}
@ -59,36 +63,78 @@ func (a *App) Run() {
}
type Handler struct {
Session *discordgo.Session
Message *discordgo.MessageCreate
Session *discordgo.Session
Message *discordgo.MessageCreate
Reference *discordgo.MessageReference
}
func (a *App) handler(session *discordgo.Session, message *discordgo.MessageCreate) {
h := &Handler{
Session: session,
Message: message,
Reference: &discordgo.MessageReference{
ChannelID: message.ChannelID,
MessageID: message.ID,
},
}
if h.Message.Author.ID == h.Session.State.User.ID {
return
}
if h.Message.Author.Bot {
return
}
var command string
var cmd string
var args string
command = h.Message.Content
command = strings.TrimSpace(command)
command = strings.TrimPrefix(command, a.Config.Prefix)
command, args, _ = strings.Cut(command, " ")
cmd = h.Message.Content
cmd = strings.TrimPrefix(cmd, a.Config.Prefix)
cmd, args, _ = strings.Cut(cmd, " ")
callback, ok := a.Commands[command]
callback, ok := a.Commands[cmd]
if !ok {
// Falling back to default help command
if cmd == "help" {
printHelp(a, h)
}
return
}
callback(h, strings.Split(args, " "))
h.Session.ChannelTyping(h.Message.ChannelID)
err := callback(h, strings.Split(args, " "))
if !err.Ok() {
printError(a, h, err)
}
}
func printHelp(a *App, h *Handler) {
var commands []string
for cmd := range a.Commands {
commands = append(commands, cmd)
}
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Title: "Help",
Description: strings.Join(commands, "\n"),
Color: utils.ColorFromRGB(255, 255, 255),
},
Reference: h.Reference,
})
}
func printError(a *App, h *Handler, e Error) {
log.Println(e.Err)
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Title: "Error",
Description: e.Msg,
Color: utils.ColorFromRGB(255, 0, 0),
},
Reference: h.Reference,
})
}

10
app/errors.go Normal file
View file

@ -0,0 +1,10 @@
package app
type Error struct {
Msg string
Err error
}
func (e *Error) Ok() bool {
return e.Err == nil
}

View file

@ -9,6 +9,8 @@ import (
"time"
"github.com/Fluffy-Bean/lynxie/app"
"github.com/Fluffy-Bean/lynxie/utils"
"github.com/bwmarrin/discordgo"
)
func RegisterMetaCommands(a *app.App) {
@ -16,8 +18,8 @@ func RegisterMetaCommands(a *app.App) {
a.RegisterCommand("debug", registerDebug(a))
}
func registerPong(a *app.App) func(h *app.Handler, args []string) {
return func(h *app.Handler, args []string) {
func registerPong(a *app.App) app.Callback {
return func(h *app.Handler, args []string) app.Error {
var options struct {
latency bool
}
@ -26,22 +28,27 @@ func registerPong(a *app.App) func(h *app.Handler, args []string) {
cmd.BoolVar(&options.latency, "latency", false, "Display the latency of ping")
cmd.Parse(args)
var content string
if options.latency {
h.Session.ChannelMessageSend(
h.Message.ChannelID,
fmt.Sprintf("Pong! %dms", h.Session.HeartbeatLatency().Milliseconds()),
)
content = fmt.Sprintf("Pong! %dms", h.Session.HeartbeatLatency().Milliseconds())
} else {
h.Session.ChannelMessageSend(
h.Message.ChannelID,
"Pong!",
)
content = "Pong!"
}
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Description: content,
Color: utils.ColorFromRGB(255, 255, 255),
},
Reference: h.Reference,
})
return app.Error{}
}
}
func registerDebug(a *app.App) func(h *app.Handler, args []string) {
return func(h *app.Handler, args []string) {
func registerDebug(a *app.App) app.Callback {
return func(h *app.Handler, args []string) app.Error {
modified := false
revision := "-"
tags := "-"
@ -65,17 +72,26 @@ func registerDebug(a *app.App) func(h *app.Handler, args []string) {
revision += " (uncommitted changes)"
}
h.Session.ChannelMessageSend(
h.Message.ChannelID,
fmt.Sprintf(
"``` Revision :: %s\nBuild Tags :: %s\nGo version :: %s\n OS/Arch :: %s\n GC Count :: %d\nLocal Time :: %s```",
revision,
tags,
_go,
runtime.GOOS+"/"+runtime.GOARCH,
gcCount,
localTime,
),
)
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Description: strings.Join(
[]string{
"```",
"Revision: " + revision,
"Build Tags: " + tags,
"Go version: " + _go,
"OS/Arch: " + runtime.GOOS + "/" + runtime.GOARCH,
"GC Count: " + fmt.Sprint(gcCount),
"Local Time: " + localTime,
"```",
},
"\n",
),
Color: utils.ColorFromRGB(255, 255, 255),
},
Reference: h.Reference,
})
return app.Error{}
}
}

View file

@ -1,46 +1,90 @@
package commands
import (
"errors"
"flag"
"fmt"
"net/http"
"slices"
"time"
"github.com/Fluffy-Bean/lynxie/app"
"github.com/Fluffy-Bean/lynxie/utils"
"github.com/bwmarrin/discordgo"
)
func RegisterTinyfoxCommands(a *app.App) {
a.RegisterCommand("animal", registerAnimal(a))
}
func registerAnimal(a *app.App) func(h *app.Handler, args []string) {
func registerAnimal(a *app.App) app.Callback {
animals := []string{
"fox", "yeen", "dog", "guara", "serval", "ott", "jackal", "bleat", "woof", "chi", "puma", "skunk", "tig", "wah",
"manul", "snep", "jaguar", "badger", "chee", "racc", "bear", "capy", "bun", "marten", "caracal", "snek",
"shiba", "dook", "leo", "yote", "poss", "chee", "lynx",
}
client := http.Client{
Timeout: 10 * time.Second,
}
return func(h *app.Handler, args []string) {
return func(h *app.Handler, args []string) app.Error {
var options struct {
animal string
}
cmd := flag.NewFlagSet("pong", flag.ContinueOnError)
cmd.StringVar(&options.animal, "animal", "wah", "Get an image of an animal!")
cmd.StringVar(&options.animal, "animal", "", "Get an image of an animal!")
cmd.Parse(args)
if options.animal == "" {
return app.Error{
Msg: "Animal name is required!",
Err: errors.New("animal name is required"),
}
}
if !slices.Contains(animals, options.animal) {
return app.Error{
Msg: fmt.Sprintf("Animal %s is invalid", options.animal),
Err: errors.New("entered invalid animal name"),
}
}
req, err := http.NewRequest(http.MethodGet, "https://api.tinyfox.dev/img?animal="+options.animal, nil)
if err != nil {
return
return app.Error{
Msg: "Failed to make request",
Err: err,
}
}
res, err := client.Do(req)
if err != nil {
return
return app.Error{
Msg: "Failed to do request",
Err: err,
}
}
defer res.Body.Close()
h.Session.ChannelFileSend(
h.Message.ChannelID,
"animal__"+options.animal+".png",
res.Body,
)
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Title: "Animal",
Image: &discordgo.MessageEmbedImage{
URL: "attachment://image.png",
},
Color: utils.ColorFromRGB(255, 255, 255),
},
Files: []*discordgo.File{
{
Name: "image.png",
ContentType: "",
Reader: res.Body,
},
},
Reference: h.Reference,
})
return app.Error{}
}
}

View file

@ -10,7 +10,7 @@ import (
func main() {
a := app.NewApp(app.Config{
Prefix: "?",
Prefix: ">",
Token: os.Getenv("TOKEN"),
Intents: discordgo.IntentsGuildMessages,
})

5
utils/color.go Normal file
View file

@ -0,0 +1,5 @@
package utils
func ColorFromRGB(r, g, b int) int {
return (r << 16) + (g << 8) + b
}