Skip to content

Commit

Permalink
refactor(discord): bot options
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew Rawlings committed Feb 7, 2024
1 parent 3daa02d commit d90653d
Show file tree
Hide file tree
Showing 19 changed files with 175 additions and 144 deletions.
15 changes: 9 additions & 6 deletions cmd/discord/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,18 @@ func main() {
authorized = append(authorized, s)
}

var guild *tempest.Snowflake
if cfg.Discord.GuildId != "" {
g, err := tempest.StringToSnowflake(cfg.Discord.GuildId)
panicOnErr(err)
guild = &g
var guilds []tempest.Snowflake
guilds = nil
if len(cfg.Discord.GuildIds) > 0 {
for _, id := range cfg.Discord.GuildIds {
s, err := tempest.StringToSnowflake(id)
panicOnErr(err)
guilds = append(guilds, s)
}
}

log.Info().Msg("Initing Discord Bot")
bot := discord.NewBot(lis, hdl, client, zerolog.DebugLevel, authorized, guild)
bot := discord.NewBot(lis, hdl, client, discord.WithAuthorizedUsers(authorized), discord.WithGuilds(guilds), discord.WithLogLevel(zerolog.DebugLevel))

go func() {
log.Info().Msg("Starting Bot")
Expand Down
116 changes: 23 additions & 93 deletions internal/app/ui/adapters/primary/discord/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/command"
comctx "github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/context"
"github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/handler"
"github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/option"
"github.com/awlsring/texit/internal/pkg/logger"
"github.com/rs/zerolog"
)
Expand All @@ -20,7 +19,7 @@ type Bot struct {
hdl *handler.Handler
lis net.Listener
authorized []tempest.Snowflake
guildId *tempest.Snowflake
guildIds []tempest.Snowflake
}

func (b *Bot) Handler() *handler.Handler {
Expand All @@ -35,17 +34,22 @@ func (b *Bot) LogLevel() zerolog.Level {
return b.logLevel
}

func NewBot(lis net.Listener, hdl *handler.Handler, tmpst *tempest.Client, lvl zerolog.Level, authorized []tempest.Snowflake, guild *tempest.Snowflake) *Bot {
return &Bot{
logLevel: lvl,
lis: lis,
tmpst: tmpst,
hdl: hdl,
authorized: authorized,
func NewBot(lis net.Listener, hdl *handler.Handler, tmpst *tempest.Client, opts ...BotOption) *Bot {
b := &Bot{
logLevel: zerolog.InfoLevel,
lis: lis,
tmpst: tmpst,
hdl: hdl,
}
for _, opt := range opts {
opt(b)
}

return b
}

func (b *Bot) registerCommands() error {
// TODO: This is gross and should be refactored
if err := b.tmpst.RegisterCommand(command.NewServerHealthCommand(b.CommandPreflight(b.hdl.ServerHealthCheck))); err != nil {
return err
}
Expand All @@ -61,11 +65,11 @@ func (b *Bot) registerCommands() error {
if err := b.tmpst.RegisterCommand(command.NewProvisionNodeCommand(b.CommandPreflight(b.hdl.ProvisionNode), b.AutoCompletePreflight(b.logLevel, func(ctx *comctx.CommandContext) []tempest.Choice {
field, input := ctx.GetFocusedValue()
switch field {
case option.ProviderName:
case command.OptionProviderName:
return b.hdl.ProviderNameAutoComplete(ctx, field, input.(string))
case option.TailnetName:
case command.OptionTailnetName:
return b.hdl.TailnetNameAutoComplete(ctx, field, input.(string))
case option.ProviderLocation:
case command.OptionProviderLocation:
return b.hdl.ProviderLocationAutoComplete(ctx, field, input.(string))
}
return nil
Expand All @@ -76,7 +80,7 @@ func (b *Bot) registerCommands() error {
if err := b.tmpst.RegisterCommand(command.NewDeprovisionNodeCommand(b.CommandPreflight(b.hdl.DeprovisionNode), b.AutoCompletePreflight(b.logLevel, func(ctx *comctx.CommandContext) []tempest.Choice {
field, input := ctx.GetFocusedValue()
switch field {
case option.NodeId:
case command.OptionNodeId:
return b.hdl.NodeIdAutoComplete(ctx, field, input.(string))
}
return nil
Expand All @@ -91,7 +95,7 @@ func (b *Bot) registerCommands() error {
if err := b.tmpst.RegisterCommand(command.NewDescribeNodeCommand(b.CommandPreflight(b.hdl.DescribeNode), b.AutoCompletePreflight(b.logLevel, func(ctx *comctx.CommandContext) []tempest.Choice {
field, input := ctx.GetFocusedValue()
switch field {
case option.NodeId:
case command.OptionNodeId:
return b.hdl.NodeIdAutoComplete(ctx, field, input.(string))
}
return nil
Expand All @@ -102,7 +106,7 @@ func (b *Bot) registerCommands() error {
if err := b.tmpst.RegisterCommand(command.NewStartNodeCommand(b.CommandPreflight(b.hdl.StartNode), b.AutoCompletePreflight(b.logLevel, func(ctx *comctx.CommandContext) []tempest.Choice {
field, input := ctx.GetFocusedValue()
switch field {
case option.NodeId:
case command.OptionNodeId:
return b.hdl.NodeIdAutoComplete(ctx, field, input.(string))
}
return nil
Expand All @@ -113,7 +117,7 @@ func (b *Bot) registerCommands() error {
if err := b.tmpst.RegisterCommand(command.NewStopNodeCommand(b.CommandPreflight(b.hdl.StopNode), b.AutoCompletePreflight(b.logLevel, func(ctx *comctx.CommandContext) []tempest.Choice {
field, input := ctx.GetFocusedValue()
switch field {
case option.NodeId:
case command.OptionNodeId:
return b.hdl.NodeIdAutoComplete(ctx, field, input.(string))
}
return nil
Expand All @@ -128,7 +132,7 @@ func (b *Bot) registerCommands() error {
if err := b.tmpst.RegisterCommand(command.NewDescribeProviderCommand(b.CommandPreflight(b.hdl.DescribeProvider), b.AutoCompletePreflight(b.logLevel, func(ctx *comctx.CommandContext) []tempest.Choice {
field, input := ctx.GetFocusedValue()
switch field {
case option.ProviderName:
case command.OptionProviderName:
return b.hdl.ProviderNameAutoComplete(ctx, field, input.(string))
}
return nil
Expand All @@ -143,7 +147,7 @@ func (b *Bot) registerCommands() error {
if err := b.tmpst.RegisterCommand(command.NewDescribeTailnetCommand(b.CommandPreflight(b.hdl.DescribeTailnet), b.AutoCompletePreflight(b.logLevel, func(ctx *comctx.CommandContext) []tempest.Choice {
field, input := ctx.GetFocusedValue()
switch field {
case option.TailnetName:
case command.OptionTailnetName:
return b.hdl.TailnetNameAutoComplete(ctx, field, input.(string))
}
return nil
Expand All @@ -154,74 +158,6 @@ func (b *Bot) registerCommands() error {
return nil
}

type CommandFunc func(*comctx.CommandContext)

func (b *Bot) auth(ctx *comctx.CommandContext) bool {
log := logger.FromContext(ctx)
log.Debug().Msg("Checking authorization")

if len(b.authorized) == 0 {
log.Debug().Msg("No authorized users, allowing")
return true
}

for _, id := range b.authorized {
if ctx.Requester() == id {
log.Debug().Msg("User is authorized")
return true
}
if ctx.RequesterRoles() != nil {
for _, r := range ctx.RequesterRoles() {
if r == id {
log.Debug().Msg("User is in authorized role")
return true
}
}
}
}

log.Debug().Msgf("User %d is not authorized", ctx.Requester())
return false
}

func (b *Bot) CommandPreflight(comFunc CommandFunc) func(itx *tempest.CommandInteraction) {
return func(itx *tempest.CommandInteraction) {
ctx, err := comctx.InitContext(b.tmpst, itx, b.logLevel)
if err != nil {
return
}
if !b.auth(ctx) {
_ = ctx.SendLinearReply("You are not authorized to use this command", true)
return
}
log := ctx.Logger()
log.Debug().Msg("Deferring command interaction")
if err := itx.Defer(true); err != nil {
log.Error().Err(err).Msg("Failed to defer command interaction")
if err = ctx.SendLinearReply("Command failed with an unknown error!", true); err != nil {
log.Error().Err(err).Msg("Failed to write bot response")
}
return
}
comFunc(ctx)
}
}

type AutoCompleteFunc func(*comctx.CommandContext) []tempest.Choice

func (b *Bot) AutoCompletePreflight(logLevel zerolog.Level, comFunc AutoCompleteFunc) func(itx tempest.CommandInteraction) []tempest.Choice {
return func(itx tempest.CommandInteraction) []tempest.Choice {
ctx, err := comctx.InitContext(b.tmpst, &itx, logLevel)
if err != nil {
return nil
}
if !b.auth(ctx) {
return nil
}
return comFunc(ctx)
}
}

func (b *Bot) Start(ctx context.Context) error {
log := logger.FromContext(ctx)

Expand All @@ -230,13 +166,7 @@ func (b *Bot) Start(ctx context.Context) error {
return err
}

var guilds []tempest.Snowflake
if b.guildId != nil {
guilds = append(guilds, *b.guildId)
} else {
guilds = nil
}
if err := b.tmpst.SyncCommands(guilds, nil, false); err != nil {
if err := b.tmpst.SyncCommands(b.guildIds, nil, false); err != nil {
return err
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package command

import (
tempest "github.com/Amatsagu/Tempest"
"github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/option"
)

func NewDescribeExecutionCommand(slash func(itx *tempest.CommandInteraction)) tempest.Command {
Expand All @@ -13,7 +12,7 @@ func NewDescribeExecutionCommand(slash func(itx *tempest.CommandInteraction)) te
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.ExecutionId,
Name: OptionExecutionId,
Description: "The ID of the execution to describe",
Required: true,
MinValue: 32,
Expand Down
17 changes: 8 additions & 9 deletions internal/app/ui/adapters/primary/discord/command/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package command

import (
tempest "github.com/Amatsagu/Tempest"
"github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/option"
)

func NewProvisionNodeCommand(slash func(itx *tempest.CommandInteraction), auto func(itx tempest.CommandInteraction) []tempest.Choice) tempest.Command {
Expand All @@ -13,31 +12,31 @@ func NewProvisionNodeCommand(slash func(itx *tempest.CommandInteraction), auto f
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.ProviderName,
Name: OptionProviderName,
Description: "The name of the provider to create the exit node in",
Required: true,
MinValue: 1,
AutoComplete: true,
},
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.TailnetName,
Name: OptionTailnetName,
Description: "The tailnet to add the exit node to.",
Required: true,
MinValue: 1,
AutoComplete: true,
},
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.ProviderLocation,
Name: OptionProviderLocation,
Description: "The location of the provider to create the exit node on",
Required: true,
MinValue: 3,
AutoComplete: true,
},
{
Type: tempest.BOOLEAN_OPTION_TYPE,
Name: option.Ephemeral,
Name: OptionEphemeral,
Description: "Whether the created exit node should be ephemeral or not. Defaults to false.",
Required: false,
},
Expand All @@ -55,7 +54,7 @@ func NewDeprovisionNodeCommand(slash func(itx *tempest.CommandInteraction), auto
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.NodeId,
Name: OptionNodeId,
Description: "The ID of the node to delete",
Required: true,
MinValue: 1,
Expand Down Expand Up @@ -84,7 +83,7 @@ func NewDescribeNodeCommand(slash func(itx *tempest.CommandInteraction), auto fu
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.NodeId,
Name: OptionNodeId,
Description: "The ID of the node to describe",
Required: true,
MinValue: 1,
Expand All @@ -104,7 +103,7 @@ func NewStartNodeCommand(slash func(itx *tempest.CommandInteraction), auto func(
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.NodeId,
Name: OptionNodeId,
Description: "The ID of the node to start",
Required: true,
MinValue: 1,
Expand All @@ -124,7 +123,7 @@ func NewStopNodeCommand(slash func(itx *tempest.CommandInteraction), auto func(i
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.NodeId,
Name: OptionNodeId,
Description: "The ID of the node to stop",
Required: true,
MinValue: 1,
Expand Down
10 changes: 10 additions & 0 deletions internal/app/ui/adapters/primary/discord/command/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package command

const (
OptionProviderName = "provider-name"
OptionProviderLocation = "provider-location"
OptionTailnetName = "tailnet-name"
OptionExecutionId = "execution-id"
OptionNodeId = "node-id"
OptionEphemeral = "ephemeral"
)
3 changes: 1 addition & 2 deletions internal/app/ui/adapters/primary/discord/command/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package command

import (
tempest "github.com/Amatsagu/Tempest"
"github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/option"
)

func NewListProvidersCommand(slash func(itx *tempest.CommandInteraction)) tempest.Command {
Expand All @@ -22,7 +21,7 @@ func NewDescribeProviderCommand(slash func(itx *tempest.CommandInteraction), aut
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.ProviderName,
Name: OptionProviderName,
Description: "The name of the provider to describe",
Required: true,
MinValue: 1,
Expand Down
3 changes: 1 addition & 2 deletions internal/app/ui/adapters/primary/discord/command/tailnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package command

import (
tempest "github.com/Amatsagu/Tempest"
"github.com/awlsring/texit/internal/app/ui/adapters/primary/discord/option"
)

func NewListTailnetsCommand(slash func(itx *tempest.CommandInteraction)) tempest.Command {
Expand All @@ -22,7 +21,7 @@ func NewDescribeTailnetCommand(slash func(itx *tempest.CommandInteraction), auto
Options: []tempest.CommandOption{
{
Type: tempest.STRING_OPTION_TYPE,
Name: option.TailnetName,
Name: OptionTailnetName,
Description: "The name of the tailnet to describe",
Required: true,
MinValue: 1,
Expand Down
2 changes: 1 addition & 1 deletion internal/app/ui/adapters/primary/discord/config/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var (
type DiscordBotConfig struct {
ApplicationId string `yaml:"applicationId"`
PublicKey string `yaml:"publicKey"`
GuildId string `yaml:"guildId"`
GuildIds []string `yaml:"guildIds"`
Token string `yaml:"token"`
Authorized []string `yaml:"authorized"`
}
Expand Down

0 comments on commit d90653d

Please sign in to comment.