Skip to content

Commit

Permalink
Removed dependency on dgc in favor of the new Slash Commands offered …
Browse files Browse the repository at this point in the history
…by the Discord API.

closes #1
  • Loading branch information
Kardbord committed Sep 20, 2021
1 parent a2673be commit f26f70e
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 167 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -18,7 +18,7 @@ This bot is not currently hosted anywhere. If you want to use it, you can always

# Hosting Installation
Hosting this bot requires a Discord Bot Token. You can generate one by visiting the [Discord Developer Portal](https://discord.com/developers/),
and then creating a new application with an accompanying bot. Be sure to give the bot its needed permissions in the OAuth2 section, and then invite it
and then creating a new application with an accompanying bot. Give the bot its needed permissions in the OAuth2 section **(Be sure to tick the "applications.commands" box!)**, and then invite it
to your server(s) using the link that is generated for you.

For now, you will have to build the Kard-bot binary yourself. Free-time permitting, I may provide a released version or docker image.
Expand Down Expand Up @@ -50,7 +50,7 @@ Useful resources for writing a Discord bot.
### Discord API Wrappers
- [discordpy](https://github.com/Rapptz/discord.py)
- [discordgo](https://github.com/bwmarrin/discordgo)
- [dgc](https://github.com/lus/dgc)
- ~~[dgc](https://github.com/lus/dgc)~~ will be deprecated April 2022 due to Discord API updates :(
- [others](https://discordapi.com/unofficial/comparison.html)

### Documentation
Expand Down
10 changes: 2 additions & 8 deletions config/config.json
@@ -1,11 +1,4 @@
{
"router": {
"prefixes": [
"!"
],
"ignoreprefixcase": true,
"botsallowed": false
},
"greetings": [
"Hello",
"Hi",
Expand All @@ -21,5 +14,6 @@
"See ya"
],
"enable-dg-logging": false,
"default-log-level": "info"
"default-log-level": "info",
"slash-cmd-guilds": null
}
10 changes: 4 additions & 6 deletions go.mod
Expand Up @@ -3,16 +3,14 @@ module github.com/TannerKvarfordt/Kard-bot
go 1.17

require (
github.com/bwmarrin/discordgo v0.23.2
github.com/bwmarrin/discordgo v0.23.3-0.20210821175000-0fad116c6c2a
github.com/joho/godotenv v1.3.0
github.com/lus/dgc v1.1.0
github.com/sirupsen/logrus v1.8.1
)

require (
github.com/gorilla/websocket v1.4.2 // indirect
github.com/karrick/tparse/v2 v2.8.2 // indirect
github.com/zekroTJA/timedmap v1.4.0 // indirect
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 // indirect
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
)
26 changes: 10 additions & 16 deletions go.sum
@@ -1,35 +1,29 @@
github.com/bwmarrin/discordgo v0.20.3/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/bwmarrin/discordgo v0.23.3-0.20210821175000-0fad116c6c2a h1:L7EuIzka83l5Z7LQqpSBfvmTNvUdr9tGhBa0mDBgSsc=
github.com/bwmarrin/discordgo v0.23.3-0.20210821175000-0fad116c6c2a/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/karrick/tparse/v2 v2.8.1/go.mod h1:OzmKMqNal7LYYHaO/Ie1f/wXmLWAaGKwJmxUFNQCVxg=
github.com/karrick/tparse/v2 v2.8.2 h1:NhvrrB7nXYa0VLn0JKn9L3oG/GZN+LB/+g5QfWE30rU=
github.com/karrick/tparse/v2 v2.8.2/go.mod h1:OzmKMqNal7LYYHaO/Ie1f/wXmLWAaGKwJmxUFNQCVxg=
github.com/lus/dgc v1.1.0 h1:Cy81DgqjUEM7tGbygWB2HTSbHSlQ4ybSzXXCmZnlcpI=
github.com/lus/dgc v1.1.0/go.mod h1:M8b1xw5H89rKgjUjZ5KwuflvGLjIsMZq07C8QWdfygU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/zekroTJA/timedmap v0.0.0-20200518230343-de9b879d109a/go.mod h1:ktlw5aYhoXQvOvWFL9SzltGXn1bQgJXxZzHJK4wQvsI=
github.com/zekroTJA/timedmap v1.4.0 h1:NIkLScX6kMzkzFP7kCIkkgKYdooAJ1itkMbJODX2WPU=
github.com/zekroTJA/timedmap v1.4.0/go.mod h1:Go4uPxMN1Wjl5IgO6HYD1tM9IQhkYEVqcrrdsI4ljXo=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
93 changes: 56 additions & 37 deletions kardbot/commandUtils.go
@@ -1,57 +1,76 @@
package kardbot

import (
"errors"
"fmt"

"github.com/lus/dgc"
"github.com/bwmarrin/discordgo"
)

// Retrieve args from context and validate they are not nil.
func getArgs(ctx *dgc.Ctx) (*dgc.Arguments, error) {
if ctx == nil {
return nil, fmt.Errorf("nil context provided")
// TODO: also add authorIsSelf method for slash commands
func authorIsOwner(i *discordgo.InteractionCreate) (bool, error) {
if getOwnerID() == "" {
return false, errors.New("owner ID is not set")
}
if i == nil {
return false, errors.New("context is nil")
}
if ctx.Arguments == nil {
return nil, fmt.Errorf("context arguments were nil")

if i.Member != nil {
if i.Member.User == nil {
return false, errors.New("member.user is nil")
}
return i.Member.User.ID == getOwnerID(), nil
} else if i.User != nil {
return i.User.ID == getOwnerID(), nil
} else {
return false, errors.New("member and user are nil")
}
return ctx.Arguments, nil
}

// Retrieve args from context and validate that they are not nil.
// Also validate that the expected number of arguments are present.
// If the "exact" argument is true, the number of present arguments
// must exactly match the number found. Otherwise, there must be
// at least as many arguments as expected.
func getArgsExpectCount(ctx *dgc.Ctx, expected int, exact bool) (*dgc.Arguments, error) {
args, err := getArgs(ctx)
if err != nil {
return nil, err
func getInteractionCreateAuthorName(i *discordgo.InteractionCreate) (string, error) {
if i == nil {
return "", errors.New("context is nil")
}

err = fmt.Errorf("unexpected arg count, expected: %d, actual: %d", expected, ctx.Arguments.Amount())
if exact && ctx.Arguments.Amount() != expected {
return nil, err
} else if ctx.Arguments.Amount() < expected {
return nil, err
if i.Member != nil {
if i.Member.User == nil {
return "", errors.New("member.user is nil")
}
return i.Member.User.Username, nil
} else if i.User != nil {
return i.User.Username, nil
} else {
return "", errors.New("member and user are nil")
}
return args, nil
}

func authorIsOwner(ctx *dgc.Ctx) (bool, error) {
if getOwnerID() == "" {
return false, fmt.Errorf("owner ID is not set")
}
if ctx == nil {
return false, fmt.Errorf("context is nil")
func getInteractionCreateAuthorID(i *discordgo.InteractionCreate) (string, error) {
if i == nil {
return "", errors.New("context is nil")
}
if ctx.Event == nil {
return false, fmt.Errorf("event is nil")
}
if ctx.Event.Author == nil {
return false, fmt.Errorf("author is nil")

if i.Member != nil {
if i.Member.User == nil {
return "", errors.New("member.user is nil")
}
return i.Member.User.ID, nil
} else if i.User != nil {
return i.User.ID, nil
} else {
return "", errors.New("member and user are nil")
}
if ctx.Event.Author.ID == getOwnerID() {
return true, nil
}

func getInteractionCreateAuthorNameAndID(i *discordgo.InteractionCreate) (string, string, error) {
id, err1 := getInteractionCreateAuthorID(i)
uname, err2 := getInteractionCreateAuthorName(i)
if err1 != nil && err2 != nil {
return uname, id, fmt.Errorf("error 1:%v\n\terror 2:%v", err1, err2)
} else if err1 != nil {
return uname, id, err1
} else if err2 != nil {
return uname, id, err2
}
return false, nil
return uname, id, nil
}
60 changes: 36 additions & 24 deletions kardbot/commands.go
Expand Up @@ -3,38 +3,50 @@ package kardbot
import (
"fmt"

"github.com/lus/dgc"
"github.com/bwmarrin/discordgo"
log "github.com/sirupsen/logrus"
)

func getCommands() []*dgc.Command {
return []*dgc.Command{
type onInteractionHandler func(*discordgo.Session, *discordgo.InteractionCreate)

func getCommands() []*discordgo.ApplicationCommand {
return []*discordgo.ApplicationCommand{
{
Name: "roll",
Aliases: []string{
"dice",
"rollDice",
},
Name: "roll",
Description: "Rolls a D{X} die {Y} times, where X and Y are provided by the user.",
Usage: "roll Y DX",
Example: "roll 1 D20",
Flags: []string{},
IgnoreCase: true,
SubCommands: []*dgc.Command{},
RateLimiter: nil,
Handler: rollDice,
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionInteger,
Name: "dice-count",
Description: "How many dice should be rolled?",
Required: true,
},
{
Type: discordgo.ApplicationCommandOptionString,
Name: "dice-sides",
Description: "How many sides on the dice? Can optionally be prefixed with 'D' or 'd'.",
Required: true,
},
},
},
{
Name: "loglevel",
Aliases: []string{},
Description: fmt.Sprintf("Update the log level of the bot.\nOnly works for whitelisted users.\nValid log levels: %v", log.AllLevels),
Usage: "loglevel LEVEL",
Example: "loglevel trace",
Flags: []string{},
IgnoreCase: true,
SubCommands: []*dgc.Command{},
RateLimiter: nil,
Handler: updateLogLevel,
Description: "Update the log level of the bot. Only works for whitelisted users.",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Name: "level",
Description: fmt.Sprintf("One of the following values: %v", log.AllLevels),
Required: true,
},
},
},
}
}

func getCommandImpls() map[string]onInteractionHandler {
return map[string]onInteractionHandler{
"roll": rollDice,
"loglevel": updateLogLevel,
}
}
31 changes: 20 additions & 11 deletions kardbot/debug.go
Expand Up @@ -6,8 +6,6 @@ import (

"github.com/bwmarrin/discordgo"
log "github.com/sirupsen/logrus"

"github.com/lus/dgc"
)

// Map logrus log levels to discordgo log levels
Expand All @@ -23,30 +21,41 @@ func logrusToDiscordGo() map[log.Level]int {
}
}

func updateLogLevel(ctx *dgc.Ctx) {
if isOwner, err := authorIsOwner(ctx); err != nil {
func updateLogLevel(s *discordgo.Session, i *discordgo.InteractionCreate) {
// TODO: move this check into a helper function
author, authorID, err := getInteractionCreateAuthorNameAndID(i)
if err != nil {
log.Error(err)
return
} else if !isOwner {
log.Warnf("User %s (%s) does not have privilege to update log level", ctx.Event.Author.Username, ctx.Event.Author.ID)
}
if authorID == s.State.User.ID {
log.Trace("Ignoring message from self")
return
}

args, err := getArgsExpectCount(ctx, 1, true)
if err != nil {
if isOwner, err := authorIsOwner(i); err != nil {
log.Error(err)
return
} else if !isOwner {
log.Warnf("User %s (%s) does not have privilege to update log level", author, authorID)
return
}
levelStr := strings.ToLower(args.Get(0).Raw())

levelStr := strings.ToLower(i.ApplicationCommandData().Options[0].StringValue())

if lvl, err := log.ParseLevel(levelStr); err == nil {
info := fmt.Sprintf(`Set logging level to "%s"`, levelStr)
log.Info(info)
ctx.RespondText(info)
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: info,
},
})
log.SetLevel(lvl)
if bot().EnableDGLogging {
// TODO: make this thread safe somehow (logrus is already thread safe)
ctx.Session.LogLevel = logrusToDiscordGo()[lvl]
s.LogLevel = logrusToDiscordGo()[lvl]
}
} else {
log.Error(err)
Expand Down

0 comments on commit f26f70e

Please sign in to comment.