disgoslash

package module
v0.7.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2021 License: MIT Imports: 15 Imported by: 6

README

disgoslash

A Golang package that provides an easy way to create serverless Discord slash command applications

tests lint Go Report Card CodeQL Coverage Status Go Reference

go get github.com/wafer-bw/disgoslash

Getting Started

This small example project uses Vercel serverless functions to execute and respond to a Discord slash command.

Prerequisites
Setup
Preliminary Note

This guide does not cover using environment variables, GitHub secrets, or Vercel secrets. Instead, secrets are used directly within the example code to keep it simpler. Please only use the credentials of the new application created while following this guide. When you are finished with the guide, delete the application and create a new one and employ environment variables and/or secrets within your code to protect your application secrets.

Discord Server
  1. Log into your Discord account and create a new server.
  2. Enable "Developer Mode" in your Discord App Settings under "Appearance".
  3. In the new server you created, right click on it's name and click on "Copy ID", this is your Guild ID.
Discord Application
  1. Create a new application here.
  2. On this page you will find both the Client ID and Public Key.
    • These two secrets should not be tracked in version control
Discord Application Bot
  1. Within the application you created above, click on "Bot" in the left pane.
  2. Click on "Add Bot".
  3. On this page you will find the bot's Token.
    • This secret should not be tracked in version control
Grant Application Access
  1. Within the application you created above, click on "OAuth2" in the left pane.
  2. Under "Scopes" check "applications.commands"
  3. Copy the URL in the textbox and go to that URL in a new browser window/tab
  4. In the "Add to Server" dropdown select the server you created above.
Add Secrets

The following secrets you have collected above should not be tracked in version control:

  • Client ID
  • Public Key
  • Token
  1. Open examples/vercel/api/index.go
  2. Fill in the secrets from above by making the following replacements
    • CLIENT_ID -> Your application Client ID
    • PUBLIC_KEY -> Your application Public Key
    • TOKEN -> Your application Token
    • GUILD_ID -> Your server Guild ID
Deploy to Vercel
  1. Ensure you are in the examples/vercel directory here
  2. Deploy to vercel
    vercel
    #> Vercel CLI 21.3.3
    #> ? Set up and deploy “~/disgoslash/examples/vercel”? [Y/n]
    y
    #> ? Which scope do you want to deploy to?
    Your Name
    #> ? Link to existing project? [y/N]
    n
    #> ? What’s your project’s name?
    hellotest
    #> ? In which directory is your code located?
    ./
    #> No framework detected. Default Project Settings:
    #> - Build Command: `npm run vercel-build` or `npm run build`
    #> - Output Directory: `public` if it exists, or `.`
    #> - Development Command: None
    #> ? Want to override the settings? [y/N]
    n
    #> 🔗  Linked to yourusername/hellotest (created .vercel and added it to .gitignore)
    #> 🔍  Inspect: https://vercel.com/yourusername/hellotest/adshjfkdashjfdsal [1s]
    #> ✅  Production: https://hellotest-tau.vercel.app [copied to clipboard] [45s]
    #> 📝  Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F).
    #> 💡  To change the domain or build command, go to https://vercel.com/yourusername/hellotest/settings
    
  3. Copy the "Production" URL from the output of running vercel.
  4. On your Discord application's General Information page set the "Interactions Endpoint URL" to the URL you copied above, adding /api to the end.
    https://hellotest-tau.vercel.app/api
    
  5. After saving those changes you should see a green bar with a message like "All your edits have been carefully recorded."
Register Slash Command
  1. Change into the sync directory
    cd sync
    
  2. Run sync
    go run sync.go
    #> Collecting outdated commands...
    #>     Guild: GLOBAL
    #>             success
    #>     Guild: 000000000000000000
    #>             success
    #> Unregistering outdated commands...
    #>     Guild: GLOBAL, Command: hello
    #>             success
    #> Registering new commands...
    #>     Guild: 000000000000000000, Command: hello
    #>             success
    #>     Guild: GLOBAL, Command: hello
    #>             success
    
Use Slash Command

In your discord server type /hello, press tab, enter a name, then hit enter. The command should run and the bot should respond.

/hello name: Bob
#> Hello Bob!

Outstanding Features

  • Stable release version
  • Internal client does not have an Edit method, it only uses Discord's create and delete endpoints right now
  • Support for syncing application command permissions
  • Some models in ./discord are not translated to Go structs yet
  • Exporter to export ApplicationCommands to JSON files
  • CLI tool to list, delete, create, update application commands

Documentation

Overview

Package disgoslash provides an easy way to create serverless Discord slash command applications

https://discord.com/developers/docs/interactions/slash-commands

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrAlreadyExists = errors.New("already exists")

ErrAlreadyExists is returned when attempting to create a command which already exists

View Source
var ErrForbidden = errors.New("forbidden - missing access")

ErrForbidden is returned when the Disord API responds with a 403

View Source
var ErrInvalidInteractionType = errors.New("invalid interaction type")

ErrInvalidInteractionType is returned when the request interaction type is invalid

View Source
var ErrMaxRetries = errors.New("max retries reached")

ErrMaxRetries is returned when the maximum number of retries is reached in a retry loop

View Source
var ErrNilInteractionResponse = errors.New("interaction response was nil")

ErrNilInteractionResponse is returned when a slash command action returns a nil interaction response

View Source
var ErrNotImplemented = errors.New("not implemented")

ErrNotImplemented is returned when whatever was requested hasn't been implemented yet

View Source
var ErrTooManyRequests = errors.New("too many requests")

ErrTooManyRequests is returned when the Disord API responds with a 429

View Source
var ErrTookTooLong = context.DeadlineExceeded.Error()

ErrTookTooLong is returned when the slash command action took longer than Discord's maximum allowed time

View Source
var ErrUnauthorized = errors.New("unauthorized")

ErrUnauthorized is returned when the request signature is invalid or Discord API responded with 401

Functions

This section is empty.

Types

type Action

Action is the function executed when a slash command interaction request is received by the Handler.

This is where you put the work to be done when a user invokes the slash command.

An action function does not include an error in its response signature because errors should be handled gracefully and a message informing the user that something went wrong should be included in the interaction response.

type Handler

type Handler struct {
	SlashCommandMap SlashCommandMap
	Creds           *discord.Credentials
}

Handler is used to handle Discord slash command interaction requests.

func (*Handler) Handle

func (handler *Handler) Handle(w http.ResponseWriter, r *http.Request)

Handle incoming interaction requests from Discord guilds, executing the SlashCommand's Action and responding with its InteractionResponse.

400 - An invalid Discord Interaction Type was passed in the request.

401 - Authorization failed.

500 - Something unexpected went wrong OR the Action did not respond within discord's maximum response time of 3 seconds.

501 - A SlashCommand that does not exist in the SlashCommandMap was requested.

Example
package main

import (
	"net/http"

	"github.com/wafer-bw/disgoslash"
	"github.com/wafer-bw/disgoslash/discord"
)

func main() {
	creds := &discord.Credentials{
		PublicKey: "YOUR_DISCORD_APPLICATION_PUBLIC_KEY",
		ClientID:  "YOUR_DISCORD_APPLICATION_CLIENT_ID",
		Token:     "YOUR_DISCORD_BOT_TOKEN",
	}

	handler := &disgoslash.Handler{
		SlashCommandMap: disgoslash.SlashCommandMap{},
		Creds:           creds,
	}
	http.HandleFunc("/", handler.Handle)
}
Output:

type SlashCommand

type SlashCommand struct {
	// The work to do when a slash command is invoked by a user
	Action Action

	// 1-32 character name matching ^[\w-]{1,32}$
	// Ex: "/tableflip"
	Name string

	// The IDs of the guilds (servers) the application command
	// should be registered to. To register the command globally
	// include a blank string ("").
	GuildIDs []string

	// The application command object schema which will be
	// registered to your Discord servers
	ApplicationCommand *discord.ApplicationCommand
}

SlashCommand holds the required information for disgoslash to execute a slash command Action and register the slash commands with Discord.

func NewSlashCommand

func NewSlashCommand(appCommand *discord.ApplicationCommand, action Action, global bool, guildIDs []string) SlashCommand

NewSlashCommand creates a new SlashCommand

Example
package main

import (
	"github.com/wafer-bw/disgoslash"
	"github.com/wafer-bw/disgoslash/discord"
)

var slashCommand disgoslash.SlashCommand

func action(request *discord.InteractionRequest) *discord.InteractionResponse {
	name, _ := request.Data.Options[0].StringValue()
	return &discord.InteractionResponse{
		Type: discord.InteractionResponseTypeChannelMessageWithSource,
		Data: &discord.InteractionApplicationCommandCallbackData{
			Content: "Hello " + name + "!",
		},
	}
}

func main() {
	isGlobal := true
	guildIDs := []string{"GUILD_ID", "ANOTHER_GUILD_ID"}
	applicationCommand := &discord.ApplicationCommand{
		Name:        "hello",
		Description: "Says hello to the user",
		Options: []*discord.ApplicationCommandOption{
			{
				Type:        discord.ApplicationCommandOptionTypeString,
				Name:        "Name",
				Description: "Enter your name",
				Required:    true,
			},
		},
		DefaultPermission: true,
	}

	slashCommand = disgoslash.NewSlashCommand(applicationCommand, action, isGlobal, guildIDs)
}
Output:

type SlashCommandMap

type SlashCommandMap map[string]SlashCommand

SlashCommandMap using each slash command's application command name as a key. Used by disgoslash Handler to serve interaction requests or by disgoslash Syncer to register slash commands with discord.

func NewSlashCommandMap

func NewSlashCommandMap(slashCommands ...SlashCommand) SlashCommandMap

NewSlashCommandMap creates a new SlashCommandMap

Example
package main

import (
	"github.com/wafer-bw/disgoslash"
)

var slashCommand disgoslash.SlashCommand
var anotherSlashCommand disgoslash.SlashCommand
var slashCommandMap disgoslash.SlashCommandMap

func main() {
	slashCommands := []disgoslash.SlashCommand{slashCommand, anotherSlashCommand}
	slashCommandMap = disgoslash.NewSlashCommandMap(slashCommands...)
}
Output:

type Syncer

type Syncer struct {
	Creds           *discord.Credentials
	SlashCommandMap SlashCommandMap
	GuildIDs        []string
	// contains filtered or unexported fields
}

Syncer is used to automatically update slash commands on Discord guilds (servers).

func (*Syncer) Sync

func (syncer *Syncer) Sync() []error

Sync your Discord application's slash commands...

Registers new commands, unregisters old commands, and reregisters existing commands.

In order for a command to be registered to a guild (server), the bot will need to be granted the "appliations.commands" scope for that server.

A global command will be registered to all servers the bot has been granted access to.

Example
package main

import (
	"github.com/wafer-bw/disgoslash"
	"github.com/wafer-bw/disgoslash/discord"
)

func main() {
	guildIDs := []string{"YOUR_GUILD_(SERVER)_ID", "ANOTHER_GUILD_ID"}
	creds := &discord.Credentials{
		PublicKey: "YOUR_DISCORD_APPLICATION_PUBLIC_KEY",
		ClientID:  "YOUR_DISCORD_APPLICATION_CLIENT_ID",
		Token:     "YOUR_DISCORD_BOT_TOKEN",
	}

	syncer := &disgoslash.Syncer{
		SlashCommandMap: disgoslash.NewSlashCommandMap(disgoslash.SlashCommand{}),
		GuildIDs:        guildIDs,
		Creds:           creds,
	}
	syncer.Sync()
}
Output:

Directories

Path Synopsis
examples
vercel/api
Package api provides a Vercel Serverless Function that uses disgoslash to serve Discord Slash Command Interactions.
Package api provides a Vercel Serverless Function that uses disgoslash to serve Discord Slash Command Interactions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL