🤖
adam
  • Getting started
  • First Steps
    • Creating Your First Command
    • Grouping Commands in Modules
    • Middlewares
    • Argument Parsing
  • Errors
    • Error Handling 101
    • Error Types
    • Changing How Errors Are Handled
  • Localization
    • Getting Started
    • How to Localize
  • Restrictions
    • What Are Restrictions?
    • Creating Custom Restrictions
Powered by GitBook
On this page
  • Creating a Command
  • Example: A Ping Command
  • Invoke's Return Values

Was this helpful?

Export as PDF
  1. First Steps

Creating Your First Command

PreviousGetting startedNextGrouping Commands in Modules

Last updated 3 years ago

Was this helpful?

A command is nothing more than a type implementing the plugin.Command interface. That interface is defined as follows:

type Command interface {
    CommandMeta
    Invoke(*state.State, *Context) (interface{}, error)
}

CommandMeta is another interface consisting of getters for the command's metadata. You don't really need to worry about the methods it requires, as the command package provides us with predefined types.

Creating a Command

Using that knowledge, you can create your first command. Create a package with your commands name, as detailed in , and add a {{package_name}}.go file to it.

Example: A Ping Command

In the ping package create a ping.go file where you create your command type. To implement the plugin.CommandMeta interface, which is part of the plugin.Command interface, you can embed command.Meta in your Ping type.

plugins/ping/ping.go
package ping

import (
    "github.com/mavolin/adam/pkg/impl/command"
    "github.com/mavolin/adam/pkg/plugin"
)

type Ping struct {
    command.Meta
}

var _ plugin.Command = new(Ping) // compile time check

This leaves your Ping command with only Invoke left, to fully satisfy plugin.Command.

plugins/ping/ping.go
import (
    ...
    "github.com/diamondburned/ariakwa/v2/state"
    "github.com/mavolin/adam/plg/plugin"
    
    "time"
)

...

func (p *Ping) Invoke(_ *state.State, ctx *plugin.Context) (interface{}, error) {
    t := time.Now()
    
    msg, err := ctx.Reply("The ping to discord is `calculating...`")
    if err != nil {
        return nil, err
    }
    
    _, err := ctx.Editf(msg.ID, "The ping to discord is %d ms", 
        time.Since(now).Milliseconds())
    return nil, err
}

Now that we've added Invoke, let's add a constructor function where we create a new Ping instance and fill the command.Meta struct we embedded earlier.

plugins/ping/ping.go
import (
    ...
    "github.com/diamondburned/arikawa/v2/discord"
    "github.com/mavolin/adam/pkg/plugin"
    "github.com/mavolin/adam/pkg/impl/command"
)

var _ plugin.Command = new(Ping) // compile-time check

// New creates a new Ping command.
func New() *Ping {
    return &Ping{
        Meta: command.Meta{
            Name:             "ping",
            ShortDescription: "Tells you the ping to Discord's servers.",
            ChannelTypes:     plugin.AllChannels,
            BotPermissions:   discord.SendMessagesPermission,
        },
    }
}
    

func (p *Ping) Invoke(_ *state.State, ctx *plugin.Context) (interface{}, error) {
    ...
}

Invoke's Return Values

As you might have noticed, plugin.Command.Invoke has two return values, and while the error return value might be a bit more self-explanatory, interface{} is probably not. So what can you use it for?

Everything you return as the first value will get turned into a message and sent in the invoking channel back to the user using the plugin.Context's Replier. Obviously, not everything can be turned into a message, at least not without causing confusion. Therefore, only the following return types are allowed:

  • uint, uint8, uint16, uint32, uint64

  • int, int8, int16, int32, int64

  • float32, float64

  • string

  • discord.Embed and *discord.Embed

  • *embedutil.Builder

  • api.SendMessageData

  • i18n.Term

  • *i18n.Config

  • any type implementingplugin.Reply

Of course, justnil is also valid, and won't create any response.

command.Meta has a lot more fields than just those we used for the Ping command. Refer to its for more information.

The second return value, error, is handed to the bot's error handler if it's not nil. While you can define a custom error handler, the default one will wrap every error not implementing errors.Error into a errors.InternalError and call Handle on it. We'll take a closer look at this later on in the chapter.

documentation
Error Types
Project Layout