Merge pull request #28 from Fluffy-Bean/go

cleany
This commit is contained in:
Michał Gdula 2025-03-31 11:36:46 +01:00 committed by GitHub
commit b234ad300b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 337 additions and 330 deletions

92
commands/debug/debug.go Normal file
View file

@ -0,0 +1,92 @@
package debug
import (
"fmt"
"runtime"
"runtime/debug"
"strings"
"time"
"github.com/Fluffy-Bean/lynxie/app"
"github.com/Fluffy-Bean/lynxie/utils"
"github.com/bwmarrin/discordgo"
)
func RegisterDebugCommands(a *app.App) {
a.RegisterCommand("debug", registerDebug(a))
}
func registerDebug(a *app.App) app.Callback {
return func(h *app.Handler, args []string) app.Error {
modified := false
revision := "-"
buildTags := "-"
goVersion := strings.TrimPrefix(runtime.Version(), "go")
gcCount := runtime.MemStats{}.NumGC
localTime := time.Now().Local().Format("2006-01-02 15:04:05")
latency := h.Session.HeartbeatLatency().Milliseconds()
info, _ := debug.ReadBuildInfo()
for _, setting := range info.Settings {
switch setting.Key {
case "vcs.revision":
revision = setting.Value
case "vcs.modified":
modified = setting.Value == "true"
case "-tags":
buildTags = strings.ReplaceAll(setting.Value, ",", " ")
}
}
if modified {
revision += " (modified)"
}
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Title: "Lynxie",
Fields: []*discordgo.MessageEmbedField{
{
Name: "Revision",
Value: revision,
Inline: false,
},
{
Name: "Build Tags",
Value: buildTags,
Inline: false,
},
{
Name: "Go version",
Value: goVersion,
Inline: false,
},
{
Name: "OS/Arch",
Value: runtime.GOOS + "/" + runtime.GOARCH,
Inline: false,
},
{
Name: "GC Count",
Value: fmt.Sprint(gcCount),
Inline: false,
},
{
Name: "Local Time",
Value: localTime,
Inline: false,
},
{
Name: "Latency",
Value: fmt.Sprintf("%dms", latency),
Inline: false,
},
},
Color: utils.ColorFromRGB(255, 255, 255),
},
Reference: h.Reference,
})
return app.Error{}
}
}

View file

@ -1,97 +0,0 @@
package commands
import (
"flag"
"fmt"
"runtime"
"runtime/debug"
"strings"
"time"
"github.com/Fluffy-Bean/lynxie/app"
"github.com/Fluffy-Bean/lynxie/utils"
"github.com/bwmarrin/discordgo"
)
func RegisterMetaCommands(a *app.App) {
a.RegisterCommand("ping", registerPong(a))
a.RegisterCommand("debug", registerDebug(a))
}
func registerPong(a *app.App) app.Callback {
return func(h *app.Handler, args []string) app.Error {
var options struct {
latency bool
}
cmd := flag.NewFlagSet("", flag.ContinueOnError)
cmd.BoolVar(&options.latency, "latency", false, "Display the latency of ping")
cmd.Parse(args)
var content string
if options.latency {
content = fmt.Sprintf("Pong! %dms", h.Session.HeartbeatLatency().Milliseconds())
} else {
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) app.Callback {
return func(h *app.Handler, args []string) app.Error {
modified := false
revision := "-"
tags := "-"
_go := strings.TrimPrefix(runtime.Version(), "go")
gcCount := runtime.MemStats{}.NumGC
localTime := time.Now().Local().Format("2006-01-02 15:04:05")
info, _ := debug.ReadBuildInfo()
for _, setting := range info.Settings {
switch setting.Key {
case "vcs.revision":
revision = setting.Value
case "vcs.modified":
modified = setting.Value == "true"
case "-tags":
tags = strings.ReplaceAll(setting.Value, ",", " ")
}
}
if modified {
revision += " (uncommitted changes)"
}
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,212 +0,0 @@
package commands
import (
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/Fluffy-Bean/lynxie/app"
"github.com/Fluffy-Bean/lynxie/utils"
"github.com/bwmarrin/discordgo"
)
func RegisterPorbCommands(a *app.App) {
a.RegisterCommand("e621", registerE621(a))
}
func registerE621(a *app.App) app.Callback {
username := os.Getenv("E621_USERNAME")
password := os.Getenv("E621_PASSWORD")
client := http.Client{
Timeout: 10 * time.Second,
}
return func(h *app.Handler, args []string) app.Error {
var options struct {
tags string
order string
rating string
}
cmd := flag.NewFlagSet("", flag.ContinueOnError)
cmd.StringVar(&options.order, "order", "random", "Search order")
cmd.StringVar(&options.rating, "rating", "e", "Search rating")
cmd.StringVar(&options.tags, "tags", "", "Search tags")
cmd.Parse(args)
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,
options.tags,
),
nil,
)
if err != nil {
return app.Error{
Msg: "Failed to make request",
Err: err,
}
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("User-Agent", fmt.Sprintf("Lynxie/1.0 (by %s on e621)", username))
req.SetBasicAuth(username, password)
res, err := client.Do(req)
if err != nil {
return app.Error{
Msg: "Failed to do request",
Err: err,
}
}
defer res.Body.Close()
var data struct {
Posts []struct {
Id int `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
File struct {
Width int `json:"width"`
Height int `json:"height"`
Ext string `json:"ext"`
Size int `json:"size"`
Md5 string `json:"md5"`
Url string `json:"url"`
} `json:"file"`
Preview struct {
Width int `json:"width"`
Height int `json:"height"`
Url string `json:"url"`
} `json:"preview"`
Sample struct {
Has bool `json:"has"`
Height int `json:"height"`
Width int `json:"width"`
Url string `json:"url"`
Alternates struct {
} `json:"alternates"`
} `json:"sample"`
Score struct {
Up int `json:"up"`
Down int `json:"down"`
Total int `json:"total"`
} `json:"score"`
Tags struct {
General []string `json:"general"`
Artist []string `json:"artist"`
Contributor []interface{} `json:"contributor"`
Copyright []string `json:"copyright"`
Character []interface{} `json:"character"`
Species []string `json:"species"`
Invalid []interface{} `json:"invalid"`
Meta []string `json:"meta"`
Lore []interface{} `json:"lore"`
} `json:"tags"`
LockedTags []interface{} `json:"locked_tags"`
ChangeSeq int `json:"change_seq"`
Flags struct {
Pending bool `json:"pending"`
Flagged bool `json:"flagged"`
NoteLocked bool `json:"note_locked"`
StatusLocked bool `json:"status_locked"`
RatingLocked bool `json:"rating_locked"`
Deleted bool `json:"deleted"`
} `json:"flags"`
Rating string `json:"rating"`
FavCount int `json:"fav_count"`
Sources []string `json:"sources"`
Pools []int `json:"pools"`
Relationships struct {
ParentId interface{} `json:"parent_id"`
HasChildren bool `json:"has_children"`
HasActiveChildren bool `json:"has_active_children"`
Children []interface{} `json:"children"`
} `json:"relationships"`
ApproverId interface{} `json:"approver_id"`
UploaderId int `json:"uploader_id"`
Description string `json:"description"`
CommentCount int `json:"comment_count"`
IsFavorited bool `json:"is_favorited"`
HasNotes bool `json:"has_notes"`
Duration interface{} `json:"duration"`
} `json:"posts"`
}
json.NewDecoder(res.Body).Decode(&data)
if len(data.Posts) == 0 {
return app.Error{
Msg: "No posts found",
Err: fmt.Errorf("no posts found"),
}
}
var description string
if len(data.Posts[0].Description) > 0 {
description = data.Posts[0].Description
} else {
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."
}
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Title: "E621",
Description: description,
Fields: []*discordgo.MessageEmbedField{
{
Name: "Score",
Value: fmt.Sprintf("⬆️ %d | ⬇️ %d", data.Posts[0].Score.Up, data.Posts[0].Score.Down),
},
{
Name: "Favorites",
Value: fmt.Sprintf("%d", data.Posts[0].FavCount),
},
{
Name: "Comments",
Value: fmt.Sprintf("%d", data.Posts[0].CommentCount),
},
{
Name: "Source(s)",
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),
),
},
Color: utils.ColorFromRGB(255, 255, 255),
},
Reference: h.Reference,
})
return app.Error{}
}
}

145
commands/porb/porb.go Normal file
View file

@ -0,0 +1,145 @@
package porb
import (
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/Fluffy-Bean/lynxie/app"
"github.com/Fluffy-Bean/lynxie/utils"
"github.com/bwmarrin/discordgo"
)
var client = http.Client{
Timeout: 10 * time.Second,
}
var username = os.Getenv("E621_USERNAME")
var password = os.Getenv("E621_PASSWORD")
func RegisterPorbCommands(a *app.App) {
a.RegisterCommand("e621", registerE621(a))
}
func registerE621(a *app.App) app.Callback {
return func(h *app.Handler, args []string) app.Error {
var options struct {
Tags string
Order string
Rating string
}
cmd := flag.NewFlagSet("", flag.ContinueOnError)
cmd.StringVar(&options.Order, "order", "random", "Search order")
cmd.StringVar(&options.Rating, "rating", "e", "Search rating")
cmd.StringVar(&options.Tags, "tags", "", "Search tags")
cmd.Parse(args)
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,
options.Tags,
),
nil,
)
if err != nil {
return app.Error{
Msg: "Failed to make request",
Err: err,
}
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("User-Agent", fmt.Sprintf("Lynxie/1.0 (by %s on e621)", username))
req.SetBasicAuth(username, password)
res, err := client.Do(req)
if err != nil {
return app.Error{
Msg: "Failed to do request",
Err: err,
}
}
defer res.Body.Close()
var data struct {
Posts []post `json:"posts"`
}
json.NewDecoder(res.Body).Decode(&data)
if len(data.Posts) == 0 {
return app.Error{
Msg: "No posts found",
Err: fmt.Errorf("no posts found"),
}
}
var description string
if len(data.Posts[0].Description) > 0 {
description = data.Posts[0].Description
} else {
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."
}
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
Embed: &discordgo.MessageEmbed{
Title: "E621",
Description: description,
Fields: []*discordgo.MessageEmbedField{
{
Name: "Score",
Value: fmt.Sprintf("⬆️ %d | ⬇️ %d", data.Posts[0].Score.Up, data.Posts[0].Score.Down),
},
{
Name: "Favorites",
Value: fmt.Sprintf("%d", data.Posts[0].FavCount),
},
{
Name: "Comments",
Value: fmt.Sprintf("%d", data.Posts[0].CommentCount),
},
{
Name: "Source(s)",
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),
),
},
Color: utils.ColorFromRGB(255, 255, 255),
},
Reference: h.Reference,
})
return app.Error{}
}
}

75
commands/porb/types.go Normal file
View file

@ -0,0 +1,75 @@
package porb
import (
"time"
)
type post struct {
Id int `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
File struct {
Width int `json:"width"`
Height int `json:"height"`
Ext string `json:"ext"`
Size int `json:"size"`
Md5 string `json:"md5"`
Url string `json:"url"`
} `json:"file"`
Preview struct {
Width int `json:"width"`
Height int `json:"height"`
Url string `json:"url"`
} `json:"preview"`
Sample struct {
Has bool `json:"has"`
Height int `json:"height"`
Width int `json:"width"`
Url string `json:"url"`
Alternates struct {
} `json:"alternates"`
} `json:"sample"`
Score struct {
Up int `json:"up"`
Down int `json:"down"`
Total int `json:"total"`
} `json:"score"`
Tags struct {
General []string `json:"general"`
Artist []string `json:"artist"`
Contributor []interface{} `json:"contributor"`
Copyright []string `json:"copyright"`
Character []interface{} `json:"character"`
Species []string `json:"species"`
Invalid []interface{} `json:"invalid"`
Meta []string `json:"meta"`
Lore []interface{} `json:"lore"`
} `json:"tags"`
LockedTags []interface{} `json:"locked_tags"`
ChangeSeq int `json:"change_seq"`
Flags struct {
Pending bool `json:"pending"`
Flagged bool `json:"flagged"`
NoteLocked bool `json:"note_locked"`
StatusLocked bool `json:"status_locked"`
RatingLocked bool `json:"rating_locked"`
Deleted bool `json:"deleted"`
} `json:"flags"`
Rating string `json:"rating"`
FavCount int `json:"fav_count"`
Sources []string `json:"sources"`
Pools []int `json:"pools"`
Relationships struct {
ParentId interface{} `json:"parent_id"`
HasChildren bool `json:"has_children"`
HasActiveChildren bool `json:"has_active_children"`
Children []interface{} `json:"children"`
} `json:"relationships"`
ApproverId interface{} `json:"approver_id"`
UploaderId int `json:"uploader_id"`
Description string `json:"description"`
CommentCount int `json:"comment_count"`
IsFavorited bool `json:"is_favorited"`
HasNotes bool `json:"has_notes"`
Duration interface{} `json:"duration"`
}

View file

@ -1,4 +1,4 @@
package commands
package tinyfox
import (
"errors"
@ -13,44 +13,46 @@ import (
"github.com/bwmarrin/discordgo"
)
var client = http.Client{
Timeout: 10 * time.Second,
}
func RegisterTinyfoxCommands(a *app.App) {
a.RegisterCommand("animal", registerAnimal(a))
}
var 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",
}
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) app.Error {
var options struct {
animal string
Kind string
}
cmd := flag.NewFlagSet("", flag.ContinueOnError)
cmd.StringVar(&options.animal, "animal", "", "Get an image of an animal!")
cmd.StringVar(&options.Kind, "kind", "", "Animal kind to search for")
cmd.Parse(args)
if options.animal == "" {
if options.Kind == "" {
return app.Error{
Msg: "Animal name is required!",
Err: errors.New("animal name is required"),
}
}
if !slices.Contains(animals, options.animal) {
if !slices.Contains(animals, options.Kind) {
return app.Error{
Msg: fmt.Sprintf("Animal %s is invalid", options.animal),
Msg: fmt.Sprintf("Animal %s is invalid", options.Kind),
Err: errors.New("entered invalid animal name"),
}
}
req, err := http.NewRequest(http.MethodGet, "https://api.tinyfox.dev/img?animal="+options.animal, nil)
req, err := http.NewRequest(http.MethodGet, "https://api.tinyfox.dev/img?animal="+options.Kind, nil)
if err != nil {
return app.Error{
Msg: "Failed to make request",

10
main.go
View file

@ -4,7 +4,9 @@ import (
"os"
"github.com/Fluffy-Bean/lynxie/app"
"github.com/Fluffy-Bean/lynxie/commands"
"github.com/Fluffy-Bean/lynxie/commands/debug"
"github.com/Fluffy-Bean/lynxie/commands/porb"
"github.com/Fluffy-Bean/lynxie/commands/tinyfox"
"github.com/bwmarrin/discordgo"
)
@ -15,9 +17,9 @@ func main() {
Intents: discordgo.IntentsGuildMessages,
})
commands.RegisterMetaCommands(a)
commands.RegisterTinyfoxCommands(a)
commands.RegisterPorbCommands(a)
debug.RegisterDebugCommands(a)
tinyfox.RegisterTinyfoxCommands(a)
porb.RegisterPorbCommands(a)
a.Run()
}