github.com/starshine-sys/bcr@v0.21.0/execute_slash.go (about)

     1  package bcr
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"sync"
     7  
     8  	"github.com/diamondburned/arikawa/v3/discord"
     9  	"github.com/diamondburned/arikawa/v3/gateway"
    10  )
    11  
    12  // InteractionCreate is called when an interaction create event is received.
    13  func (r *Router) InteractionCreate(ic *gateway.InteractionCreateEvent) {
    14  	if ic.Type != gateway.CommandInteraction {
    15  		return
    16  	}
    17  
    18  	ctx, err := r.NewSlashContext(ic)
    19  	if err != nil {
    20  		r.Logger.Error("Couldn't create slash context: %v", err)
    21  		return
    22  	}
    23  
    24  	err = r.ExecuteSlash(ctx)
    25  	if err != nil {
    26  		r.Logger.Error("Couldn't create slash context: %v", err)
    27  	}
    28  }
    29  
    30  // ExecuteSlash executes slash commands. Only one layer for now, so no subcommands, sorry :(
    31  func (r *Router) ExecuteSlash(ctx *SlashContext) (err error) {
    32  	err = r.executeSlash(true, ctx, r.cmds, &r.cmdMu)
    33  	if err == errCommandRun {
    34  		return nil
    35  	}
    36  	return
    37  }
    38  
    39  func errCommand(err error) error {
    40  	if err == nil {
    41  		return errCommandRun
    42  	}
    43  	return err
    44  }
    45  
    46  func (r *Router) executeSlash(isTopLevel bool, ctx *SlashContext, cmds map[string]*Command, mu *sync.RWMutex) (err error) {
    47  	// first, check subcommands
    48  	if len(ctx.CommandOptions) > 0 && isTopLevel {
    49  		for _, g := range r.slashGroups {
    50  			if strings.EqualFold(g.Name, ctx.CommandName) {
    51  				nctx := &SlashContext{}
    52  				*nctx = *ctx
    53  				nctx.CommandName = ctx.CommandOptions[0].Name
    54  				nctx.CommandOptions = ctx.CommandOptions[0].Options
    55  
    56  				// convert subcommands slice to a map
    57  				m := map[string]*Command{}
    58  				for _, cmd := range g.Subcommands {
    59  					m[strings.ToLower(cmd.Name)] = cmd
    60  				}
    61  				var nmu sync.RWMutex // this doesn't matter so we just create a new one
    62  
    63  				return r.executeSlash(false, nctx, m, &nmu)
    64  			}
    65  		}
    66  	}
    67  
    68  	// else, we try top-level commands (or skip to this immediately if it isn't the top level)
    69  	mu.RLock()
    70  	cmd, ok := cmds[ctx.CommandName]
    71  	if !ok || cmd.SlashCommand == nil {
    72  		mu.RUnlock()
    73  		err = ctx.SendEphemeral(fmt.Sprintf("Looks like you found a command (``%v``) that's registered as a slash command, but doesn't work as one :(\nPlease report this to the bot developer as this is a bug!", EscapeBackticks(ctx.CommandName)))
    74  		return errCommand(err)
    75  	}
    76  	mu.RUnlock()
    77  
    78  	ctx.Command = cmd
    79  
    80  	if (cmd.GuildOnly || cmd.Permissions != 0) && !ctx.Event.GuildID.IsValid() {
    81  		err = ctx.SendEphemeral(":x: This command cannot be run in DMs.")
    82  		return errCommand(err)
    83  	}
    84  
    85  	if r.BlacklistFunc != nil && cmd.Blacklistable {
    86  		if r.BlacklistFunc(ctx) {
    87  			err = ctx.SendEphemeral("This command can't be used here.")
    88  			return errCommand(err)
    89  		}
    90  	}
    91  
    92  	if cmd.GuildPermissions != 0 {
    93  		if ctx.Guild == nil || ctx.Member == nil {
    94  			err = ctx.SendEphemeral(":x: This command cannot be used in DMs.")
    95  			return errCommand(err)
    96  		}
    97  		if !ctx.GuildPerms().Has(cmd.GuildPermissions) {
    98  			err = ctx.SendEphemeral(fmt.Sprintf(":x: You are not allowed to use this command. You are missing the following permissions:\n> ```%v```", strings.Join(PermStrings(cmd.GuildPermissions), ", ")))
    99  			return errCommand(err)
   100  		}
   101  	}
   102  
   103  	if cmd.Permissions != 0 {
   104  		if ctx.Member != nil && ctx.Guild != nil {
   105  			if !discord.CalcOverwrites(*ctx.Guild, *ctx.Channel, *ctx.Member).Has(cmd.Permissions) {
   106  				err = ctx.SendEphemeral(fmt.Sprintf(":x: You are not allowed to use this command. You are missing the following permissions:\n> ```%v```", strings.Join(PermStrings(cmd.Permissions), ", ")))
   107  				return errCommand(err)
   108  			}
   109  		} else {
   110  			err = ctx.SendEphemeral(":x: This command cannot be run in DMs.")
   111  			return errCommand(err)
   112  		}
   113  	}
   114  
   115  	if cmd.OwnerOnly {
   116  		isOwner := false
   117  		for _, u := range ctx.Router.BotOwners {
   118  			if ctx.Author.ID.String() == u {
   119  				isOwner = true
   120  				break
   121  			}
   122  		}
   123  
   124  		if !isOwner {
   125  			err = ctx.SendEphemeral(":x: This command can only be used by a bot owner.")
   126  			return errCommand(err)
   127  		}
   128  	}
   129  
   130  	if cmd.CustomPermissions != nil {
   131  		b, err := cmd.CustomPermissions.Check(ctx)
   132  		// if it errored, send that error and return
   133  		if err != nil {
   134  			err = ctx.SendEphemeral(fmt.Sprintf(":x: An internal error occurred when checking your permissions.\nThe following permission(s) could not be checked:\n> ```%s```", cmd.CustomPermissions))
   135  			return errCommand(err)
   136  		}
   137  
   138  		// else if it returned false, show that error and return
   139  		if !b {
   140  			err = ctx.SendEphemeral(fmt.Sprintf(":x: You are not allowed to use this command. You are missing the following permission(s):\n> ```%s```", cmd.CustomPermissions))
   141  			return errCommand(err)
   142  		}
   143  	}
   144  
   145  	err = cmd.SlashCommand(ctx)
   146  	return errCommand(err)
   147  }