diff --git a/.woodpecker/pipeline.yaml b/.woodpecker/pipeline.yaml index f2d32d6..bf7397a 100644 --- a/.woodpecker/pipeline.yaml +++ b/.woodpecker/pipeline.yaml @@ -7,7 +7,7 @@ steps: image: golang:1.24-alpine3.20 commands: - go mod tidy - - go build -ldflags="-X 'main.ConfigBuildHash=$CI_COMMIT_SHA' -X 'main.ConfigBuildPipeline=$CI_PIPELINE_URL'" -o lynxie . + - go build -ldflags="-X 'github.com/Fluffy-Bean/lynxie/_resources.BuildHash=$CI_COMMIT_SHA' -X 'github.com/Fluffy-Bean/lynxie/_resources.BuildPipelineLink=$CI_PIPELINE_URL'" -o lynxie . - name: deploy image: alpine:latest commands: diff --git a/_resources/resources.go b/_resources/resources.go index 4151a5e..f79e07a 100644 --- a/_resources/resources.go +++ b/_resources/resources.go @@ -6,3 +6,6 @@ import ( //go:embed fonts/Roboto.ttf var FontRoboto []byte + +var BuildHash string +var BuildPipelineLink string diff --git a/internal/err/err.go b/internal/err/err.go deleted file mode 100644 index ab6de65..0000000 --- a/internal/err/err.go +++ /dev/null @@ -1,10 +0,0 @@ -package err - -type Error struct { - Msg string - Err error -} - -func (e *Error) Ok() bool { - return e.Err == nil -} diff --git a/app/errors.go b/internal/errors/errors.go similarity index 86% rename from app/errors.go rename to internal/errors/errors.go index b95580f..a92f65a 100644 --- a/app/errors.go +++ b/internal/errors/errors.go @@ -1,4 +1,4 @@ -package app +package errors type Error struct { Msg string diff --git a/app/app.go b/internal/handler/handler.go similarity index 58% rename from app/app.go rename to internal/handler/handler.go index beba236..132cc94 100644 --- a/app/app.go +++ b/internal/handler/handler.go @@ -1,67 +1,65 @@ -package app +package handler import ( "fmt" - "log" "os" "os/signal" "strings" "syscall" "github.com/Fluffy-Bean/lynxie/internal/color" + "github.com/Fluffy-Bean/lynxie/internal/errors" "github.com/bwmarrin/discordgo" ) -type Callback func(h *Handler, args []string) Error +type Callback func(h *Handler, args []string) errors.Error -type Config struct { - BotPrefix string - BotToken string - BotIntents discordgo.Intent - CommandExtras map[string]string +type Bot struct { + Prefix string + token string + intents discordgo.Intent + commands map[string]Callback + aliases map[string]string } -type App struct { - Config Config - Commands map[string]Callback - CommandAliases map[string]string -} - -func NewApp(config Config) *App { - return &App{ - Config: config, - Commands: make(map[string]Callback), - CommandAliases: make(map[string]string), +func NewBot(prefix, token string, intents discordgo.Intent) *Bot { + return &Bot{ + Prefix: prefix, + token: token, + intents: intents, + commands: make(map[string]Callback), + aliases: make(map[string]string), } } -func (a *App) RegisterCommand(cmd string, f Callback) { - a.Commands[cmd] = f +func (b *Bot) RegisterCommand(cmd string, f Callback) { + b.commands[cmd] = f } -func (a *App) RegisterCommandAlias(alias, cmd string) { - a.CommandAliases[alias] = cmd +func (b *Bot) RegisterCommandAlias(alias, cmd string) { + b.aliases[alias] = cmd } -func (a *App) Run() { - dg, err := discordgo.New("Bot " + a.Config.BotToken) +func (b *Bot) Run() { + dg, err := discordgo.New("Bot " + b.token) if err != nil { - fmt.Println("error creating Discord session,", err) + fmt.Println("Could not create Discord session:", err) return } - dg.AddHandler(a.handler) - dg.Identify.Intents = a.Config.BotIntents + dg.AddHandler(b.handler) + dg.Identify.Intents = b.intents err = dg.Open() if err != nil { - fmt.Println("error opening connection,", err) + fmt.Println("Could not connect:", err) return } fmt.Println("Bot is now running. Press CTRL-C to exit.") + sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) <-sc @@ -75,7 +73,7 @@ type Handler struct { Reference *discordgo.MessageReference } -func (a *App) handler(session *discordgo.Session, message *discordgo.MessageCreate) { +func (b *Bot) handler(session *discordgo.Session, message *discordgo.MessageCreate) { h := &Handler{ Session: session, Message: message, @@ -87,8 +85,8 @@ func (a *App) handler(session *discordgo.Session, message *discordgo.MessageCrea defer func() { if r := recover(); r != nil { - printError(a, h, Error{ - Msg: "But the bot simply refused", + printError(b, h, errors.Error{ + Msg: "But the b simply refused", Err: fmt.Errorf("%v", r), }) } @@ -105,19 +103,19 @@ func (a *App) handler(session *discordgo.Session, message *discordgo.MessageCrea var args string cmd = h.Message.Content - cmd = strings.TrimPrefix(cmd, a.Config.BotPrefix) + cmd = strings.TrimPrefix(cmd, b.Prefix) cmd, args, _ = strings.Cut(cmd, " ") - alias, ok := a.CommandAliases[cmd] + alias, ok := b.aliases[cmd] if ok { cmd = alias } - callback, ok := a.Commands[cmd] + callback, ok := b.commands[cmd] if !ok { // Falling back to default help command if cmd == "help" { - printHelp(a, h) + printHelp(b, h) } return @@ -127,13 +125,14 @@ func (a *App) handler(session *discordgo.Session, message *discordgo.MessageCrea err := callback(h, strings.Split(args, " ")) if !err.Ok() { - printError(a, h, err) + printError(b, h, err) } } -func printHelp(a *App, h *Handler) { +func printHelp(bot *Bot, h *Handler) { var commands []string - for cmd := range a.Commands { + + for cmd := range bot.commands { commands = append(commands, cmd) } @@ -147,8 +146,8 @@ func printHelp(a *App, h *Handler) { }) } -func printError(a *App, h *Handler, e Error) { - log.Println(e.Err) +func printError(bot *Bot, h *Handler, e errors.Error) { + fmt.Println(e.Err) _, _ = h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{ Embed: &discordgo.MessageEmbed{ diff --git a/main.go b/main.go index 379ff12..8c246a4 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,7 @@ package main import ( "os" - "github.com/Fluffy-Bean/lynxie/app" + "github.com/Fluffy-Bean/lynxie/internal/handler" "github.com/Fluffy-Bean/lynxie/pkg/commands/debug" "github.com/Fluffy-Bean/lynxie/pkg/commands/img" "github.com/Fluffy-Bean/lynxie/pkg/commands/porb" @@ -11,26 +11,13 @@ import ( "github.com/bwmarrin/discordgo" ) -var ConfigBuildHash string -var ConfigBuildPipeline string - func main() { - a := app.NewApp(app.Config{ - BotPrefix: ">", - BotToken: os.Getenv("TOKEN"), - BotIntents: discordgo.IntentsGuildMessages, - CommandExtras: map[string]string{ - "debug_build-hash": ConfigBuildHash, - "debug_build-pipeline": ConfigBuildPipeline, - "e621_username": os.Getenv("E621_USERNAME"), - "e621_password": os.Getenv("E621_PASSWORD"), - }, - }) + bot := handler.NewBot(">", os.Getenv("TOKEN"), discordgo.IntentsGuildMessages) - debug.RegisterDebugCommands(a) - img.RegisterImgCommands(a) - tinyfox.RegisterTinyfoxCommands(a) - porb.RegisterPorbCommands(a) + debug.RegisterDebugCommands(bot) + img.RegisterImgCommands(bot) + tinyfox.RegisterTinyfoxCommands(bot) + porb.RegisterPorbCommands(bot) - a.Run() + bot.Run() } diff --git a/pkg/commands/debug/debug.go b/pkg/commands/debug/debug.go index 3d9cb59..a402931 100644 --- a/pkg/commands/debug/debug.go +++ b/pkg/commands/debug/debug.go @@ -6,22 +6,24 @@ import ( "runtime/debug" "strings" - "github.com/Fluffy-Bean/lynxie/app" + "github.com/Fluffy-Bean/lynxie/_resources" "github.com/Fluffy-Bean/lynxie/internal/color" + "github.com/Fluffy-Bean/lynxie/internal/errors" + "github.com/Fluffy-Bean/lynxie/internal/handler" "github.com/bwmarrin/discordgo" ) -func RegisterDebugCommands(a *app.App) { - a.RegisterCommand("debug", registerDebug(a)) +func RegisterDebugCommands(bot *handler.Bot) { + bot.RegisterCommand("debug", registerDebug(bot)) } -func registerDebug(a *app.App) app.Callback { - return func(h *app.Handler, args []string) app.Error { +func registerDebug(bot *handler.Bot) handler.Callback { + return func(h *handler.Handler, args []string) errors.Error { buildTags := "-" goVersion := strings.TrimPrefix(runtime.Version(), "go") gcCount := runtime.MemStats{}.NumGC - buildHash, _ := a.Config.CommandExtras["debug_build-hash"] - buildPipeline, _ := a.Config.CommandExtras["debug_build-pipeline"] + buildHash := _resources.BuildHash + buildPipeline := _resources.BuildPipelineLink latency := h.Session.HeartbeatLatency().Milliseconds() info, _ := debug.ReadBuildInfo() @@ -72,12 +74,12 @@ func registerDebug(a *app.App) app.Callback { Reference: h.Reference, }) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to send debug message", Err: err, } } - return app.Error{} + return errors.Error{} } } diff --git a/pkg/commands/img/img.go b/pkg/commands/img/img.go index 3148864..3640677 100644 --- a/pkg/commands/img/img.go +++ b/pkg/commands/img/img.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" _ "embed" - "errors" "fmt" "image" "image/jpeg" @@ -16,8 +15,9 @@ import ( "git.sr.ht/~sbinet/gg" "github.com/Fluffy-Bean/lynxie/_resources" - "github.com/Fluffy-Bean/lynxie/app" "github.com/Fluffy-Bean/lynxie/internal/color" + "github.com/Fluffy-Bean/lynxie/internal/errors" + "github.com/Fluffy-Bean/lynxie/internal/handler" "github.com/bwmarrin/discordgo" ) @@ -27,19 +27,19 @@ var client = http.Client{ Timeout: 10 * time.Second, } -func RegisterImgCommands(a *app.App) { - a.RegisterCommand("saveable", registerSaveable(a)) - a.RegisterCommandAlias("gif", "saveable") +func RegisterImgCommands(bot *handler.Bot) { + bot.RegisterCommand("saveable", registerSaveable(bot)) + bot.RegisterCommandAlias("gif", "saveable") - a.RegisterCommand("caption", registerCaption(a)) - a.RegisterCommandAlias("c", "caption") + bot.RegisterCommand("caption", registerCaption(bot)) + bot.RegisterCommandAlias("c", "caption") } -func registerSaveable(a *app.App) app.Callback { - return func(h *app.Handler, args []string) app.Error { +func registerSaveable(bot *handler.Bot) handler.Callback { + return func(h *handler.Handler, args []string) errors.Error { fileEndpoint, err := findClosestImage(h) if err != nil { - return app.Error{ + return errors.Error{ Msg: "Could not get image", Err: err, } @@ -47,23 +47,23 @@ func registerSaveable(a *app.App) app.Callback { req, err := http.NewRequest(http.MethodGet, fileEndpoint, nil) if err != nil { - return app.Error{ + return errors.Error{ Msg: "", Err: err, } } if req.ContentLength > maxFileSize { - return app.Error{ + return errors.Error{ Msg: "Could not get image", - Err: errors.New("requested file is too big"), + Err: fmt.Errorf("requested file is too big"), } } res, err := client.Do(req) if err != nil { - return app.Error{ - Msg: "", + return errors.Error{ + Msg: "failed to fetch image", Err: err, } } @@ -88,21 +88,21 @@ func registerSaveable(a *app.App) app.Callback { Reference: h.Reference, }) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to send saveable message", Err: err, } } - return app.Error{} + return errors.Error{} } } -func registerCaption(a *app.App) app.Callback { - return func(h *app.Handler, args []string) app.Error { +func registerCaption(bot *handler.Bot) handler.Callback { + return func(h *handler.Handler, args []string) errors.Error { fileEndpoint, err := findClosestImage(h) if err != nil { - return app.Error{ + return errors.Error{ Msg: "Could not get image", Err: err, } @@ -110,23 +110,23 @@ func registerCaption(a *app.App) app.Callback { req, err := http.NewRequest(http.MethodGet, fileEndpoint, nil) if err != nil { - return app.Error{ - Msg: "", + return errors.Error{ + Msg: "failed to fetch image", Err: err, } } if req.ContentLength > maxFileSize { - return app.Error{ + return errors.Error{ Msg: "Could not get image", - Err: errors.New("requested file is too big"), + Err: fmt.Errorf("requested file is too big"), } } res, err := client.Do(req) if err != nil { - return app.Error{ - Msg: "", + return errors.Error{ + Msg: "failed to fetch image", Err: err, } } @@ -134,7 +134,7 @@ func registerCaption(a *app.App) app.Callback { buff, err := io.ReadAll(res.Body) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to read image", Err: err, } @@ -142,9 +142,9 @@ func registerCaption(a *app.App) app.Callback { img, err := loadImageFromBytes(buff) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to load image", - Err: errors.New("Failed to load image " + err.Error()), + Err: fmt.Errorf("Failed to load image " + err.Error()), } } imgWidth, imgHeight := img.Bounds().Dx(), img.Bounds().Dy() @@ -167,7 +167,7 @@ func registerCaption(a *app.App) app.Callback { canvas := gg.NewContext(imgWidth, imgHeight+captionHeight) err = canvas.LoadFontFaceFromBytes(_resources.FontRoboto, captionSize) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to load font", Err: err, } @@ -193,7 +193,7 @@ func registerCaption(a *app.App) app.Callback { &jpeg.Options{Quality: 100}, ) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to encode JPEG", Err: err, } @@ -217,13 +217,13 @@ func registerCaption(a *app.App) app.Callback { Reference: h.Reference, }) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to send caption message", Err: err, } } - return app.Error{} + return errors.Error{} } } @@ -255,11 +255,11 @@ func loadImageFromBytes(buff []byte) (image.Image, error) { return img, nil } -func findClosestImage(h *app.Handler) (string, error) { +func findClosestImage(h *handler.Handler) (string, error) { // Get message attachments if len(h.Message.Attachments) >= 1 { if h.Message.Attachments[0].Size > maxFileSize { - return "", errors.New("file size is too big") + return "", fmt.Errorf("file size is too big") } return h.Message.Attachments[0].ProxyURL, nil @@ -269,7 +269,7 @@ func findClosestImage(h *app.Handler) (string, error) { if h.Message.ReferencedMessage != nil { if len(h.Message.ReferencedMessage.Attachments) >= 1 { if h.Message.ReferencedMessage.Attachments[0].Size > maxFileSize { - return "", errors.New("file size is too big") + return "", fmt.Errorf("file size is too big") } return h.Message.ReferencedMessage.Attachments[0].ProxyURL, nil @@ -282,7 +282,7 @@ func findClosestImage(h *app.Handler) (string, error) { } } - return "", errors.New("no files exists") + return "", fmt.Errorf("no files exists") } func measureText(font []byte, text string, size float64, width int) (int, int) { diff --git a/pkg/commands/porb/porb.go b/pkg/commands/porb/porb.go index 270a1b4..549a264 100644 --- a/pkg/commands/porb/porb.go +++ b/pkg/commands/porb/porb.go @@ -4,13 +4,13 @@ import ( "encoding/json" "flag" "fmt" - "log" "net/http" "strings" "time" - "github.com/Fluffy-Bean/lynxie/app" "github.com/Fluffy-Bean/lynxie/internal/color" + "github.com/Fluffy-Bean/lynxie/internal/errors" + "github.com/Fluffy-Bean/lynxie/internal/handler" "github.com/bwmarrin/discordgo" ) @@ -53,23 +53,14 @@ type post struct { CommentCount int `json:"comment_count"` } -func RegisterPorbCommands(a *app.App) { - username, _ := a.Config.CommandExtras["e621_username"] - password, _ := a.Config.CommandExtras["e621_password"] +func RegisterPorbCommands(bot *handler.Bot) { + bot.RegisterCommand("e621", registerE621(bot)) - if username == "" || password == "" { - log.Println("Not registering e621 command...") - - return - } - - a.RegisterCommand("e621", registerE621(a)) - - a.RegisterCommandAlias("porb", "e621") + bot.RegisterCommandAlias("porb", "e621") } -func registerE621(a *app.App) app.Callback { - return func(h *app.Handler, args []string) app.Error { +func registerE621(bot *handler.Bot) handler.Callback { + return func(h *handler.Handler, args []string) errors.Error { var options struct { Order string Rating string @@ -82,40 +73,33 @@ func registerE621(a *app.App) app.Callback { err := cmd.Parse(args) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed parsing e621 flags", Err: err, } } - req, err := http.NewRequest( - http.MethodGet, - fmt.Sprintf( - "https://e621.net/posts.json/?limit=1&tags=order:%s+rating:%s+%s", - options.Order, - options.Rating, - strings.Join(cmd.Args(), "+"), - ), - nil, + url := fmt.Sprintf( + "https://e621.net/posts.json/?limit=1&tags=order:%s+rating:%s+%s", + options.Order, + options.Rating, + strings.Join(cmd.Args(), "+"), ) + + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to make request", Err: err, } } - username, _ := a.Config.CommandExtras["e621_username"] - password, _ := a.Config.CommandExtras["e621_password"] - req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") - req.Header.Add("User-Agent", fmt.Sprintf("Lynxie/2.0 (by %s on e621)", username)) - req.SetBasicAuth(username, password) res, err := client.Do(req) if err != nil { - return app.Error{ + return errors.Error{ Msg: "Failed to do request", Err: err, } @@ -127,14 +111,14 @@ func registerE621(a *app.App) app.Callback { } err = json.NewDecoder(res.Body).Decode(&data) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed decoding e621 response", Err: err, } } if len(data.Posts) == 0 { - return app.Error{ + return errors.Error{ Msg: "no posts found", Err: fmt.Errorf("no posts found"), } @@ -147,13 +131,6 @@ func registerE621(a *app.App) app.Callback { description = "No description provided." } - var generalTags string - if len(data.Posts[0].Tags.General) > 0 { - generalTags = strings.Join(data.Posts[0].Tags.General[:20], ", ") - } else { - generalTags = "No tags provided." - } - _, err = h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{ Embed: &discordgo.MessageEmbed{ Title: "E621", @@ -176,33 +153,24 @@ func registerE621(a *app.App) app.Callback { Value: strings.Join(data.Posts[0].Sources, ", "), Inline: false, }, - { - Name: "Tag(s)", - Value: generalTags, - Inline: false, - }, }, Image: &discordgo.MessageEmbedImage{ URL: data.Posts[0].File.Url, }, Footer: &discordgo.MessageEmbedFooter{ - Text: fmt.Sprintf( - "ID: %d | Created: %s", - data.Posts[0].Id, - data.Posts[0].CreatedAt.Format(time.DateTime), - ), + Text: fmt.Sprintf("ID: %d | Created: %s", data.Posts[0].Id, data.Posts[0].CreatedAt.Format(time.DateTime)), }, Color: color.RGBToDiscord(255, 255, 255), }, Reference: h.Reference, }) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed sending e621 message", Err: err, } } - return app.Error{} + return errors.Error{} } } diff --git a/pkg/commands/tinyfox/tinyfox.go b/pkg/commands/tinyfox/tinyfox.go index 0431443..c6334c2 100644 --- a/pkg/commands/tinyfox/tinyfox.go +++ b/pkg/commands/tinyfox/tinyfox.go @@ -1,15 +1,15 @@ package tinyfox import ( - "errors" "fmt" "net/http" "slices" "strings" "time" - "github.com/Fluffy-Bean/lynxie/app" "github.com/Fluffy-Bean/lynxie/internal/color" + "github.com/Fluffy-Bean/lynxie/internal/errors" + "github.com/Fluffy-Bean/lynxie/internal/handler" "github.com/bwmarrin/discordgo" ) @@ -80,18 +80,18 @@ var animalAliases = map[string]string{ "opossum": "poss", } -func RegisterTinyfoxCommands(a *app.App) { - a.RegisterCommand("animal", registerAnimal(a)) +func RegisterTinyfoxCommands(bot *handler.Bot) { + bot.RegisterCommand("animal", registerAnimal(bot)) - a.RegisterCommandAlias("a", "animal") + bot.RegisterCommandAlias("bot", "animal") } -func registerAnimal(a *app.App) app.Callback { - return func(h *app.Handler, args []string) app.Error { +func registerAnimal(bot *handler.Bot) handler.Callback { + return func(h *handler.Handler, args []string) errors.Error { if len(args) < 1 { - return app.Error{ + return errors.Error{ Msg: "Animal name is required!", - Err: errors.New("animal name is required"), + Err: fmt.Errorf("animal name is required"), } } @@ -100,9 +100,9 @@ func registerAnimal(a *app.App) app.Callback { if !slices.Contains(animals, animal) { alias, ok := animalAliases[animal] if !ok { - return app.Error{ + return errors.Error{ Msg: fmt.Sprintf("Animal \"%s\" is invalid. The following animals are supported:\n%s", animal, strings.Join(animals, ", ")), - Err: errors.New("entered invalid animal name"), + Err: fmt.Errorf("entered invalid animal name"), } } animal = alias @@ -110,7 +110,7 @@ func registerAnimal(a *app.App) app.Callback { req, err := http.NewRequest(http.MethodGet, "https://api.tinyfox.dev/img?animal="+animal, nil) if err != nil { - return app.Error{ + return errors.Error{ Msg: "Failed to make request", Err: err, } @@ -118,7 +118,7 @@ func registerAnimal(a *app.App) app.Callback { res, err := client.Do(req) if err != nil { - return app.Error{ + return errors.Error{ Msg: "Failed to do request", Err: err, } @@ -143,12 +143,12 @@ func registerAnimal(a *app.App) app.Callback { Reference: h.Reference, }) if err != nil { - return app.Error{ + return errors.Error{ Msg: "failed to send tinyfox message", Err: err, } } - return app.Error{} + return errors.Error{} } }