discord: Refactor interactions and components

This commit gets rid of contain-it-all structs and instead opt for
interface union types containing underlying concrete types with no
overloading.

The code is much more verbose by doing this, but the API is much nicer
to use. The only disadvantage in that regard is the interface assertion
being too verbose and risky for users at times.
This commit is contained in:
diamondburned 2021-10-10 15:44:31 -07:00
parent 8abd5bc86e
commit 331ec59dec
22 changed files with 1879 additions and 730 deletions

View File

@ -19,82 +19,85 @@ func main() {
token := os.Getenv("BOT_TOKEN")
if token == "" {
log.Fatalln("No $BOT_TOKEN given.")
log.Fatalln("no $BOT_TOKEN given")
}
s, err := session.New("Bot " + token)
if err != nil {
log.Fatalln("Session failed:", err)
log.Fatalln("session failed:", err)
return
}
app, err := s.CurrentApplication()
if err != nil {
log.Fatalln("Failed to get application ID:", err)
log.Fatalln("failed to get application ID:", err)
}
appID := app.ID
s.AddHandler(func(e *gateway.InteractionCreateEvent) {
if e.Type == discord.CommandInteraction {
var resp api.InteractionResponse
switch data := e.Data.(type) {
case *discord.CommandInteraction:
if data.Name != "buttons" {
resp = api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Unknown command: " + data.Name),
},
}
break
}
// Send a message with a button back on slash commands.
data := api.InteractionResponse{
resp = api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("This is a message with a button!"),
Components: &[]discord.Component{
Components: discord.ComponentsPtr(
&discord.ActionRowComponent{
Components: []discord.Component{
&discord.ButtonComponent{
Label: "Hello World!",
CustomID: "first_button",
Emoji: &discord.ButtonEmoji{
Name: "👋",
},
Style: discord.PrimaryButton,
},
&discord.ButtonComponent{
Label: "Secondary",
CustomID: "second_button",
Style: discord.SecondaryButton,
},
&discord.ButtonComponent{
Label: "Success",
CustomID: "success_button",
Style: discord.SuccessButton,
},
&discord.ButtonComponent{
Label: "Danger",
CustomID: "danger_button",
Style: discord.DangerButton,
},
&discord.ButtonComponent{
Label: "Link",
URL: "https://google.com",
Style: discord.LinkButton,
},
&discord.ButtonComponent{
Label: "Hello World!",
CustomID: "first_button",
Emoji: &discord.ComponentEmoji{Name: "👋"},
Style: discord.PrimaryButtonStyle(),
},
&discord.ButtonComponent{
Label: "Secondary",
CustomID: "second_button",
Style: discord.SecondaryButtonStyle(),
},
&discord.ButtonComponent{
Label: "Success",
CustomID: "success_button",
Style: discord.SuccessButtonStyle(),
},
&discord.ButtonComponent{
Label: "Danger",
CustomID: "danger_button",
Style: discord.DangerButtonStyle(),
},
},
},
// This is automatically put into its own row.
&discord.ButtonComponent{
Label: "Link",
Style: discord.LinkButtonStyle("https://google.com"),
},
),
},
}
if err := s.RespondInteraction(e.ID, e.Token, data); err != nil {
log.Println("failed to send interaction callback:", err)
case discord.ComponentInteraction:
resp = api.InteractionResponse{
Type: api.UpdateMessage,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Custom ID: " + string(data.ID())),
},
}
}
if e.Type != discord.ComponentInteraction {
default:
log.Printf("unknown interaction type %T", e.Data)
return
}
customID := e.Data.(*discord.ComponentInteractionData).CustomID
data := api.InteractionResponse{
Type: api.UpdateMessage,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Custom ID: " + customID),
},
}
if err := s.RespondInteraction(e.ID, e.Token, data); err != nil {
if err := s.RespondInteraction(e.ID, e.Token, resp); err != nil {
log.Println("failed to send interaction callback:", err)
}
})
@ -125,6 +128,8 @@ func main() {
},
}
log.Println("Creating guild commands...")
for _, command := range newCommands {
_, err := s.CreateGuildCommand(appID, guildID, command)
if err != nil {
@ -132,6 +137,8 @@ func main() {
}
}
log.Println("Guild commands created. Bot is ready.")
// Block forever.
select {}
}

View File

@ -8,7 +8,7 @@ import (
"github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/session"
"github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/utils/json/option"
)
@ -22,7 +22,7 @@ func main() {
log.Fatalln("No $BOT_TOKEN given.")
}
s, err := session.New("Bot " + token)
s, err := state.New("Bot " + token)
if err != nil {
log.Fatalln("Session failed:", err)
return
@ -32,7 +32,6 @@ func main() {
if err != nil {
log.Fatalln("Failed to get application ID:", err)
}
appID := app.ID
s.AddHandler(func(e *gateway.InteractionCreateEvent) {
data := api.InteractionResponse{
@ -57,7 +56,7 @@ func main() {
log.Println("Gateway connected. Getting all guild commands.")
commands, err := s.GuildCommands(appID, guildID)
commands, err := s.GuildCommands(app.ID, guildID)
if err != nil {
log.Fatalln("failed to get guild commands:", err)
}
@ -74,7 +73,7 @@ func main() {
}
for _, command := range newCommands {
_, err := s.CreateGuildCommand(appID, guildID, command)
_, err := s.CreateGuildCommand(app.ID, guildID, command)
if err != nil {
log.Fatalln("failed to create guild command:", err)
}

View File

@ -21,11 +21,11 @@ func (c *Client) CurrentApplication() (*discord.Application, error) {
// https://discord.com/developers/docs/interactions/slash-commands#create-global-application-command-json-params
type CreateCommandData struct {
Name string `json:"name"`
Description string `json:"description"`
Options []discord.CommandOption `json:"options,omitempty"`
NoDefaultPermission bool `json:"-"`
Type discord.CommandType `json:"type,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Options discord.CommandOptions `json:"options,omitempty"`
NoDefaultPermission bool `json:"-"`
Type discord.CommandType `json:"type,omitempty"`
}
func (c CreateCommandData) MarshalJSON() ([]byte, error) {

View File

@ -64,7 +64,7 @@ type InteractionResponseData struct {
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components is the list of components (such as buttons) to be attached to
// the message.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for the message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
// Flags are the interaction application command callback data flags.
@ -162,7 +162,7 @@ type EditInteractionResponseData struct {
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for the message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep.

View File

@ -287,7 +287,7 @@ type EditMessageData struct {
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for a message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep

View File

@ -108,7 +108,7 @@ type SendMessageData struct {
Files []sendpart.File `json:"-"`
// Components is the list of components (such as buttons) to be attached to
// the message.
Components []discord.Component `json:"components,omitempty"`
Components discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for a message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`

View File

@ -147,7 +147,7 @@ type ExecuteData struct {
// Components is the list of components (such as buttons) to be attached to
// the message.
Components []discord.Component `json:"components,omitempty"`
Components discord.ContainerComponents `json:"components,omitempty"`
// Files represents a list of files to upload. This will not be
// JSON-encoded and will only be available through WriteMultipart.
@ -238,7 +238,7 @@ type EditMessageData struct {
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for a message.
AllowedMentions *api.AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep

View File

@ -1,20 +1,14 @@
package discord
import (
"time"
"github.com/diamondburned/arikawa/v3/utils/json"
)
type Application struct {
// ID is the ID of the app.
ID AppID `json:"id"`
// Name is the name of the app.
Name string `json:"string"`
Name string `json:"name"`
// Icon is the icon hash of the app.
Icon *Hash `json:"icon"`
// Description is the description of the app.
Description string `json:"string"`
Description string `json:"description"`
// RPCOrigins is the RPC origin urls, if RPC is enabled.
RPCOrigins []string `json:"rpc_origins"`
// BotPublic is whether users besides the app owner can join the app's bot
@ -54,6 +48,7 @@ type Application struct {
// Slug is the URL slug that links to the game's store page.
Slug string `json:"slug"`
}
type ApplicationFlags uint32
const (
@ -96,161 +91,6 @@ const (
MembershipAccepted
)
// Command is the base "command" model that belongs to an application. This is
// what you are creating when you POST a new command.
//
// https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-structure
type Command struct {
// ID is the unique id of the command.
ID CommandID `json:"id"`
// Type is the type of command.
Type CommandType `json:"type,omitempty"`
// AppID is the unique id of the parent application.
AppID AppID `json:"application_id"`
// GuildID is the guild id of the command, if not global.
GuildID GuildID `json:"guild_id,omitempty"`
// Name is the 1-32 lowercase character name matching ^[\w-]{1,32}$.
Name string `json:"name"`
// Description is the 1-100 character description.
Description string `json:"description"`
// Options are the parameters for the command.
//
// Note that required options must be listed before optional options, and
// a command, or each individual subcommand, can have a maximum of 25
// options.
//
// It is only present on ChatInputCommands.
Options []CommandOption `json:"options,omitempty"`
// NoDefaultPermissions defines whether the command is NOT enabled by
// default when the app is added to a guild.
NoDefaultPermission bool `json:"-"`
// Version is an autoincrementing version identifier updated during
// substantial record changes
Version Snowflake `json:"version,omitempty"`
}
type CommandType uint
const (
ChatInputCommand CommandType = iota + 1
UserCommand
MessageCommand
)
func (c Command) MarshalJSON() ([]byte, error) {
type RawCommand Command
cmd := struct {
RawCommand
DefaultPermission bool `json:"default_permission"`
}{RawCommand: RawCommand(c)}
// Discord defaults default_permission to true, so we need to invert the
// meaning of the field (>No<DefaultPermission) to match Go's default
// value, false.
cmd.DefaultPermission = !c.NoDefaultPermission
return json.Marshal(cmd)
}
func (c *Command) UnmarshalJSON(data []byte) error {
type RawCommand Command
cmd := struct {
*RawCommand
DefaultPermission bool `json:"default_permission"`
}{RawCommand: (*RawCommand)(c)}
if err := json.Unmarshal(data, &cmd); err != nil {
return err
}
// Discord defaults default_permission to true, so we need to invert the
// meaning of the field (>No<DefaultPermission) to match Go's default
// value, false.
c.NoDefaultPermission = !cmd.DefaultPermission
// Discord defaults type to 1 if omitted.
if c.Type == 0 {
c.Type = ChatInputCommand
}
return nil
}
// CreatedAt returns a time object representing when the command was created.
func (c Command) CreatedAt() time.Time {
return c.ID.Time()
}
type CommandOption struct {
Type CommandOptionType `json:"type"`
Name string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
// Choices cannot be present if Autocomplete is true.
Choices []CommandOptionChoice `json:"choices,omitempty"`
Autocomplete bool `json:"autocomplete"`
Options []CommandOption `json:"options,omitempty"`
// If this option is a channel type, the channels shown will be restricted to these types
ChannelTypes []ChannelType `json:"-"`
}
func (c CommandOption) MarshalJSON() ([]byte, error) {
type RawOption CommandOption
option := struct {
RawOption
ChannelTypes []uint16 `json:"channel_types,omitempty"`
}{RawOption: RawOption(c)}
// []uint8 is marshalled as a base64 string, so we marshal a []uint64 instead.
if len(c.ChannelTypes) > 0 {
option.ChannelTypes = make([]uint16, 0, len(c.ChannelTypes))
for _, t := range c.ChannelTypes {
option.ChannelTypes = append(option.ChannelTypes, uint16(t))
}
}
return json.Marshal(option)
}
func (c *CommandOption) UnmarshalJSON(data []byte) error {
type RawOption CommandOption
cmd := struct {
*RawOption
ChannelTypes []uint16 `json:"channel_types,omitempty"`
}{RawOption: (*RawOption)(c)}
if err := json.Unmarshal(data, &cmd); err != nil {
return err
}
c.ChannelTypes = make([]ChannelType, 0, len(cmd.ChannelTypes))
for _, t := range cmd.ChannelTypes {
c.ChannelTypes = append(c.ChannelTypes, ChannelType(t))
}
return nil
}
type CommandOptionType uint
const (
SubcommandOption CommandOptionType = iota + 1
SubcommandGroupOption
StringOption
IntegerOption
BooleanOption
UserOption
ChannelOption
RoleOption
MentionableOption
NumberOption
)
type CommandOptionChoice struct {
Name string `json:"name"`
Value string `json:"value"`
}
// https://discord.com/developers/docs/interactions/slash-commands#application-command-permissions-object-guild-application-command-permissions-structure
type GuildCommandPermissions struct {
ID CommandID `json:"id"`

View File

@ -144,9 +144,11 @@ func (ch Channel) IconURLWithType(t ImageType) string {
ch.ID.String() + "/" + t.format(ch.Icon)
}
type ChannelType uint8
// ChannelType describes the type of the channel.
//
// https://discord.com/developers/docs/resources/channel#channel-object-channel-types
type ChannelType uint16
const (
// GuildText is a text channel within a server.
GuildText ChannelType = iota

580
discord/command.go Normal file
View File

@ -0,0 +1,580 @@
package discord
import (
"fmt"
"time"
"github.com/diamondburned/arikawa/v3/utils/json"
"github.com/pkg/errors"
)
// CommandType is the type of the command, which describes the intended
// invokation source of the command.
type CommandType uint
const (
ChatInputCommand CommandType = iota + 1
UserCommand
MessageCommand
)
// Command is the base "command" model that belongs to an application. This is
// what you are creating when you POST a new command.
//
// https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-structure
type Command struct {
// ID is the unique id of the command.
ID CommandID `json:"id"`
// Type is the intended source of the command.
Type CommandType `json:"type,omitempty"`
// AppID is the unique id of the parent application.
AppID AppID `json:"application_id"`
// GuildID is the guild id of the command, if not global.
GuildID GuildID `json:"guild_id,omitempty"`
// Name is the 1-32 lowercase character name matching ^[\w-]{1,32}$.
Name string `json:"name"`
// Description is the 1-100 character description.
Description string `json:"description"`
// Options are the parameters for the command. Its types are value types,
// which can either be a SubcommandOption or a SubcommandGroupOption.
//
// Note that required options must be listed before optional options, and
// a command, or each individual subcommand, can have a maximum of 25
// options.
//
// It is only present on ChatInputCommands.
Options CommandOptions `json:"options,omitempty"`
// NoDefaultPermissions defines whether the command is NOT enabled by
// default when the app is added to a guild.
NoDefaultPermission bool `json:"-"`
// Version is an autoincrementing version identifier updated during
// substantial record changes
Version Snowflake `json:"version,omitempty"`
}
// CreatedAt returns a time object representing when the command was created.
func (c *Command) CreatedAt() time.Time {
return c.ID.Time()
}
func (c *Command) MarshalJSON() ([]byte, error) {
type RawCommand Command
cmd := struct {
*RawCommand
DefaultPermission bool `json:"default_permission"`
}{RawCommand: (*RawCommand)(c)}
// Discord defaults default_permission to true, so we need to invert the
// meaning of the field (>No<DefaultPermission) to match Go's default
// value, false.
cmd.DefaultPermission = !c.NoDefaultPermission
return json.Marshal(cmd)
}
func (c *Command) UnmarshalJSON(data []byte) error {
type rawCommand Command
cmd := struct {
*rawCommand
DefaultPermission bool `json:"default_permission"`
}{
rawCommand: (*rawCommand)(c),
}
if err := json.Unmarshal(data, &cmd); err != nil {
return err
}
// Discord defaults default_permission to true, so we need to invert the
// meaning of the field (>No<DefaultPermission) to match Go's default
// value, false.
c.NoDefaultPermission = !cmd.DefaultPermission
// Discord defaults type to 1 if omitted.
if c.Type == 0 {
c.Type = ChatInputCommand
}
return nil
}
// commandTypeCheckError is returned if a one of Command's Options fails the
// type check.
type commandTypeCheckError struct {
name string
got interface{}
expect string
}
// Name returns the name of the erroneous command.
func (err commandTypeCheckError) Name() string {
return err.name
}
// Data returns the erroneous data that belongs to this error. It is usually
// either a CommandOption or a CommandOptionValue.
func (err commandTypeCheckError) Data() interface{} {
return err.got
}
// Error implements error.
func (err commandTypeCheckError) Error() string {
return fmt.Sprintf(
"error at option name %q: expected %s, got %T",
err.name, err.expect, err.got,
)
}
// CommandOptions is used primarily for unmarshaling.
type CommandOptions []CommandOption
// UnmarshalJSON unmarshals b into these CommandOptions.
func (c *CommandOptions) UnmarshalJSON(b []byte) error {
var unknowns []UnknownCommandOption
if err := json.Unmarshal(b, &unknowns); err != nil {
return err
}
if len(unknowns) == 0 {
*c = nil
return nil
}
*c = make([]CommandOption, len(unknowns))
for i, v := range unknowns {
(*c)[i] = v.data
}
return nil
}
// UnknownCommandOption is used for unknown or unmarshaled CommandOption values.
// It is used in the unmarshaling stage for all CommandOption types.
//
// An UnknownCommandOption will satisfy both CommandOption and
// CommandOptionValue. Code that type-switches on either of them should not
// assume that only the expected types are used.
type UnknownCommandOption struct {
OptionName string `json:"name"`
OptionType CommandOptionType `json:"type"`
raw json.Raw
data CommandOption
}
// Name returns the supposeed name for this UnknownCommandOption.
func (u *UnknownCommandOption) Name() string {
return u.OptionName
}
// Type returns the supposed type for this UnknownCommandOption.
func (u *UnknownCommandOption) Type() CommandOptionType {
return u.OptionType
}
// Raw returns the raw JSON of this UnknownCommandOption. It will only return a
// non-nil blob of JSON if the command option's type cannot be found. If this
// method doesn't return nil, then Data's type will be UnknownCommandOption.
func (u *UnknownCommandOption) Raw() json.Raw {
return u.raw
}
// Data returns the underlying data type, which is a type that satisfies either
// CommandOption or CommandOptionValue.
func (u *UnknownCommandOption) Data() CommandOption {
return u.data
}
// Implement both CommandOption and CommandOptionValue.
func (u *UnknownCommandOption) _val() {}
// UnmarshalJSON parses the JSON into the struct as-is then reads all its
// children Options/Choices (if subcommand(group)). Typed command options are
// created into u.Data, or u.Raw if the type is unknown. This is done from the
// bottom up.
func (u *UnknownCommandOption) UnmarshalJSON(b []byte) error {
type unknown UnknownCommandOption
if err := json.Unmarshal(b, (*unknown)(u)); err != nil {
return errors.Wrap(err, "failed to unmarshal unknown")
}
switch u.Type() {
case SubcommandOptionType:
u.data = &SubcommandOption{}
case SubcommandGroupOptionType:
u.data = &SubcommandGroupOption{}
case StringOptionType:
u.data = &StringOption{}
case IntegerOptionType:
u.data = &IntegerOption{}
case BooleanOptionType:
u.data = &BooleanOption{}
case UserOptionType:
u.data = &UserOption{}
case ChannelOptionType:
u.data = &ChannelOption{}
case RoleOptionType:
u.data = &RoleOption{}
case MentionableOptionType:
u.data = &MentionableOption{}
case NumberOptionType:
u.data = &NumberOption{}
default:
// Copy the blob of bytes into a new slice.
u.raw = append(json.Raw(nil), b...)
u.data = u
return nil
}
if err := json.Unmarshal(b, u.data); err != nil {
return errors.Wrapf(err, "failed to unmarshal type %d", u.Type())
}
return nil
}
// CommandOptionType is the enumerated integer type for command options. The
// user usually won't have to touch any of these enum constants.
type CommandOptionType uint
const (
SubcommandOptionType CommandOptionType = iota + 1
SubcommandGroupOptionType
StringOptionType
IntegerOptionType
BooleanOptionType
UserOptionType
ChannelOptionType
RoleOptionType
MentionableOptionType
NumberOptionType
maxOptionType // for bound checking
)
// CommandOption is a union of command option types. The constructors for
// CommandOption will hint the types that can be a CommandOption.
type CommandOption interface {
Name() string
Type() CommandOptionType
}
// Maintaining these structs is quite an effort. If a new field is added into
// the generic CommandOption type, you MUST update ALL CommandOption structs.
// This means copy-pasting, yes.
// SubcommandGroupOption is a subcommand group that fits into a CommandOption.
type SubcommandGroupOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Subcommands []*SubcommandOption `json:"options"`
}
// Name implements CommandOption.
func (s *SubcommandGroupOption) Name() string { return s.OptionName }
// Type implements CommandOption.
func (s *SubcommandGroupOption) Type() CommandOptionType { return SubcommandGroupOptionType }
// SubcommandOption is a subcommand option that fits into a CommandOption.
type SubcommandOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
// Options contains command option values. All CommandOption types except
// for SubcommandOption and SubcommandGroupOption will implement this
// interface.
Options []CommandOptionValue `json:"options"`
}
// Name implements CommandOption.
func (s *SubcommandOption) Name() string { return s.OptionName }
// Type implements CommandOption.
func (s *SubcommandOption) Type() CommandOptionType { return SubcommandOptionType }
// UnmarshalJSON unmarshals the given JSON bytes. It actually does
// type-checking.
func (s *SubcommandOption) UnmarshalJSON(b []byte) error {
type raw SubcommandOption
var opt struct {
*raw
Type CommandOptionType `json:"type"`
Options []UnknownCommandOption `json:"options"`
}
opt.raw = (*raw)(s)
if err := json.Unmarshal(b, &opt); err != nil {
return err
}
if opt.Type != SubcommandOptionType {
return fmt.Errorf("unexpected (not SubcommandOption) type %d", s.Type())
}
s.Options = make([]CommandOptionValue, len(opt.Options))
for i, opt := range opt.Options {
ov, ok := opt.data.(CommandOptionValue)
if !ok {
return commandTypeCheckError{opt.OptionName, opt.data, "CommandOptionValue"}
}
s.Options[i] = ov
}
return nil
}
// CommandOptionValue is a subcommand option that fits into a subcommand.
type CommandOptionValue interface {
CommandOption
_val()
}
// StringOption is a subcommand option that fits into a CommandOptionValue.
type StringOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []StringChoice `json:"choices,omitempty"`
Autocomplete bool `json:"autocomplete"`
}
// Name implements CommandOption.
func (s *StringOption) Name() string { return s.OptionName }
// Type implements CommandOptionValue.
func (s *StringOption) Type() CommandOptionType { return StringOptionType }
func (s *StringOption) _val() {}
// StringChoice is a pair of string key to a string.
type StringChoice struct {
Name string `json:"name"`
Value string `json:"value"`
}
// IntegerOption is a subcommand option that fits into a CommandOptionValue.
type IntegerOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []IntegerChoice `json:"choices,omitempty"`
}
// Name implements CommandOption.
func (i *IntegerOption) Name() string { return i.OptionName }
// Type implements CommandOptionValue.
func (i *IntegerOption) Type() CommandOptionType { return IntegerOptionType }
func (i *IntegerOption) _val() {}
// IntegerChoice is a pair of string key to an integer.
type IntegerChoice struct {
Name string `json:"name"`
Value int `json:"value"`
}
// BooleanOption is a subcommand option that fits into a CommandOptionValue.
type BooleanOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []BooleanChoice `json:"choices,omitempty"`
}
// Name implements CommandOption.
func (b *BooleanOption) Name() string { return b.OptionName }
// Type implements CommandOptionValue.
func (b *BooleanOption) Type() CommandOptionType { return BooleanOptionType }
func (b *BooleanOption) _val() {}
// BooleanChoice is a pair of string key to a boolean.
type BooleanChoice struct {
Name string `json:"name"`
Value bool `json:"value"`
}
// UserOption is a subcommand option that fits into a CommandOptionValue.
type UserOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []UserChoice `json:"choices,omitempty"`
}
// Name implements CommandOption.
func (u *UserOption) Name() string { return u.OptionName }
// Type implements CommandOptionValue.
func (u *UserOption) Type() CommandOptionType { return UserOptionType }
func (u *UserOption) _val() {}
// UserChoice is a pair of string key to a user ID.
type UserChoice struct {
Name string `json:"name"`
Value UserID `json:"value,string"`
}
// ChannelOption is a subcommand option that fits into a CommandOptionValue.
type ChannelOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []ChannelChoice `json:"choices,omitempty"`
ChannelTypes []ChannelType `json:"channel_types,omitempty"`
}
// Name implements CommandOption.
func (c *ChannelOption) Name() string { return c.OptionName }
// Type implements CommandOptionValue.
func (c *ChannelOption) Type() CommandOptionType { return ChannelOptionType }
func (c *ChannelOption) _val() {}
// ChannelChoice is a pair of string key to a channel ID.
type ChannelChoice struct {
Name string `json:"name"`
Value ChannelID `json:"value,string"`
}
// RoleOption is a subcommand option that fits into a CommandOptionValue.
type RoleOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []RoleChoice `json:"choices,omitempty"`
}
// Name implements CommandOption.
func (r *RoleOption) Name() string { return r.OptionName }
// Type implements CommandOptionValue.
func (r *RoleOption) Type() CommandOptionType { return RoleOptionType }
func (r *RoleOption) _val() {}
// RoleChoice is a pair of string key to a role ID.
type RoleChoice struct {
Name string `json:"name"`
Value RoleID `json:"value,string"`
}
// MentionableOption is a subcommand option that fits into a CommandOptionValue.
type MentionableOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []MentionableChoice `json:"choices,omitempty"`
}
// Name implements CommandOption.
func (m *MentionableOption) Name() string { return m.OptionName }
// Type implements CommandOptionValue.
func (m *MentionableOption) Type() CommandOptionType { return MentionableOptionType }
func (m *MentionableOption) _val() {}
// MentionableChoice is a pair of string key to a mentionable snowflake IDs. To
// use this correctly, use the Resolved field.
type MentionableChoice struct {
Name string `json:"name"`
Value Snowflake `json:"value,string"`
}
// NumberOption is a subcommand option that fits into a CommandOptionValue.
type NumberOption struct {
OptionName string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Choices []NumberChoice `json:"choices,omitempty"`
}
// Name implements CommandOption.
func (n *NumberOption) Name() string { return n.OptionName }
// Type implements CommandOptionValue.
func (n *NumberOption) Type() CommandOptionType { return NumberOptionType }
func (n *NumberOption) _val() {}
// NumberChoice is a pair of string key to a float64 values.
type NumberChoice struct {
Name string `json:"name"`
Value float64 `json:"value"`
}
// Generated with utils/generate-option-marshalers.sh
// MarshalJSON marshals SubcommandOption to JSON with the "type" field.
func (s *SubcommandOption) MarshalJSON() ([]byte, error) {
type raw SubcommandOption
return json.Marshal(struct {
Type CommandOptionType `json:"type"`
*raw
}{
Type: s.Type(),
raw: (*raw)(s),
})
}
// MarshalJSON marshals SubcommandGroupOption to JSON with the "type" field.
func (s *SubcommandGroupOption) MarshalJSON() ([]byte, error) {
type raw SubcommandGroupOption
return json.Marshal(struct {
Type CommandOptionType `json:"type"`
*raw
}{
Type: s.Type(),
raw: (*raw)(s),
})
}
// MarshalJSON marshals StringOption to JSON with the "type" field.
func (s *StringOption) MarshalJSON() ([]byte, error) {
type raw StringOption
return json.Marshal(struct {
Type CommandOptionType `json:"type"`
*raw
}{
Type: s.Type(),
raw: (*raw)(s),
})
}
// MarshalJSON marshals IntegerOption to JSON with the "type" field.
func (i *IntegerOption) MarshalJSON() ([]byte, error) {
type raw IntegerOption
return json.Marshal(struct {
Type CommandOptionType `json:"type"`
*raw
}{
Type: i.Type(),
raw: (*raw)(i),
})
}
// MarshalJSON marshals BooleanOption to JSON with the "type" field.
func (b *BooleanOption) MarshalJSON() ([]byte, error) {
type raw BooleanOption
return json.Marshal(struct {
Type CommandOptionType `json:"type"`
*raw
}{
Type: b.Type(),
raw: (*raw)(b),
})
}
// MarshalJSON marshals UserOption to JSON with the "type" field.
func (u *UserOption) MarshalJSON() ([]byte, error) {
type raw UserOption
return json.Marshal(struct {
Type CommandOptionType `json:"type"`
*raw
}{
Type: u.Type(),
raw: (*raw)(u),
})
}

View File

@ -1,246 +1,455 @@
package discord
import (
"errors"
"fmt"
"github.com/diamondburned/arikawa/v3/utils/json"
"github.com/diamondburned/arikawa/v3/utils/json/option"
"github.com/pkg/errors"
)
var ErrNestedActionRow = errors.New("action row cannot have action row as a child")
// ComponentType is the type of a component.
type ComponentType uint
const (
ActionRowComponentType ComponentType = iota + 1
_ ComponentType = iota
ActionRowComponentType
ButtonComponentType
SelectComponentType
)
// ComponentWrap wraps Component for the purpose of JSON unmarshalling.
// Type assertions should be made on Component to access the underlying data.
// The underlying types of the Component are pointer types.
type ComponentWrap struct {
Component Component
}
// UnwrapComponents returns a slice of the underlying component interfaces.
func UnwrapComponents(wraps []ComponentWrap) []Component {
components := make([]Component, len(wraps))
for i, w := range wraps {
components[i] = w.Component
}
return components
}
// Type returns the underlying component's type.
func (c *ComponentWrap) Type() ComponentType {
return c.Component.Type()
}
// MarshalJSON marshals the component in the format Discord expects.
func (c *ComponentWrap) MarshalJSON() ([]byte, error) {
return c.Component.MarshalJSON()
}
// UnmarshalJSON unmarshals json into the component.
func (c *ComponentWrap) UnmarshalJSON(b []byte) error {
var t struct {
Type ComponentType `json:"type"`
}
err := json.Unmarshal(b, &t)
if err != nil {
return err
}
switch t.Type {
// String formats Type's name as a string.
func (t ComponentType) String() string {
switch t {
case ActionRowComponentType:
c.Component = &ActionRowComponent{}
return "ActionRow"
case ButtonComponentType:
c.Component = &ButtonComponent{}
return "Button"
case SelectComponentType:
return "Select"
default:
c.Component = &UnknownComponent{typ: t.Type}
return fmt.Sprintf("ComponentType(%d)", int(t))
}
return json.Unmarshal(b, c.Component)
}
// Component is a component that can be attached to an interaction response.
type Component interface {
json.Marshaler
Type() ComponentType
}
// ContainerComponents is primarily used for unmarshaling. It is the top-level
// type for component lists.
type ContainerComponents []ContainerComponent
// ActionRowComponent is a row of components at the bottom of a message.
type ActionRowComponent struct {
Components []Component `json:"components"`
}
// Type implements the Component interface.
func (*ActionRowComponent) Type() ComponentType {
return ActionRowComponentType
}
// MarshalJSON marshals the action row in the format Discord expects.
func (a ActionRowComponent) MarshalJSON() ([]byte, error) {
type actionRow ActionRowComponent
return json.Marshal(struct {
actionRow
Type ComponentType `json:"type"`
}{
actionRow: actionRow(a),
Type: ActionRowComponentType,
})
}
// UnmarshalJSON unmarshals json into the components.
func (a *ActionRowComponent) UnmarshalJSON(b []byte) error {
type actionRow ActionRowComponent
type rowTypes struct {
Components []struct {
Type ComponentType `json:"type"`
} `json:"components"`
}
var r rowTypes
err := json.Unmarshal(b, &r)
if err != nil {
// UnmarshalJSON unmarshals JSON into the component. It does type-checking and
// will only accept container components.
func (c *ContainerComponents) UnmarshalJSON(b []byte) error {
var jsons []json.Raw
if err := json.Unmarshal(b, &jsons); err != nil {
return err
}
a.Components = make([]Component, len(r.Components))
for i, t := range r.Components {
switch t.Type {
case ActionRowComponentType:
// ActionRow cannot have child components of type Actionrow
return ErrNestedActionRow
case ButtonComponentType:
a.Components[i] = &ButtonComponent{}
default:
a.Components[i] = &UnknownComponent{typ: t.Type}
*c = make([]ContainerComponent, len(jsons))
for i, b := range jsons {
p, err := ParseComponent(b)
if err != nil {
return err
}
}
alias := actionRow(*a)
err = json.Unmarshal(b, &alias)
if err != nil {
return err
cc, ok := p.(ContainerComponent)
if !ok {
return fmt.Errorf("expected container, got %T", p)
}
(*c)[i] = cc
}
*a = ActionRowComponent(alias)
return nil
}
// ButtonComponent is a clickable button that may be added to an interaction
// response.
type ButtonComponent struct {
Label string `json:"label"`
// CustomID attached to InteractionCreate event when clicked.
CustomID string `json:"custom_id"`
Style ButtonStyle `json:"style"`
Emoji *ButtonEmoji `json:"emoji,omitempty"`
// URL is only present on link-style buttons.
URL URL `json:"url,omitempty"`
Disabled bool `json:"disabled,omitempty"`
// Component is a component that can be attached to an interaction response. A
// Component is either an InteractiveComponent or a ContainerComponent. See
// those appropriate types for more information.
type Component interface {
// Type returns the type of the underlying component.
Type() ComponentType
_cmp()
}
// InteractiveComponent extends the Component for components that are
// interactible, or components that aren't containers (like ActionRow). This is
// useful for ActionRow to type-check that no nested ActionRows are allowed.
type InteractiveComponent interface {
Component
// ID returns the ID of the underlying component.
ID() ComponentID
_icp()
}
// ContainerComponent is the opposite of InteractiveComponent: it describes
// components that only contain other components. The only component that
// satisfies that is ActionRow.
type ContainerComponent interface {
Component
_ctn()
}
// NewComponent returns a new Component from the given type that's matched with
// the global ComponentFunc map. If the type is unknown, then Unknown is used.
func ParseComponent(b []byte) (Component, error) {
var t struct {
Type ComponentType
}
if err := json.Unmarshal(b, &t); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal component type")
}
var c Component
switch t.Type {
case ActionRowComponentType:
c = &ActionRowComponent{}
case ButtonComponentType:
c = &ButtonComponent{}
case SelectComponentType:
c = &SelectComponent{}
default:
c = &UnknownComponent{typ: t.Type}
}
if err := json.Unmarshal(b, c); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal component body")
}
return c, nil
}
// ActionRow is a row of components at the bottom of a message. Its type,
// InteractiveComponent, ensures that only non-ActionRow components are allowed
// on it.
type ActionRowComponent []InteractiveComponent
// Components wraps the given list of components inside ActionRows if it's not
// already in one. This is a convenient function that wraps components inside
// ActionRows for the user. It panics if any of the action rows have nested
// action rows in them.
//
// Here's an example of how to use it:
//
// discord.Components(
// discord.TextButtonComponent("Hello, world!"),
// discord.Components(
// discord.TextButtonComponent("Hello!"),
// discord.TextButtonComponent("Delete."),
// ),
// )
//
func Components(components ...Component) ContainerComponents {
new := make([]ContainerComponent, len(components))
for i, comp := range components {
cc, ok := comp.(ContainerComponent)
if !ok {
// Wrap. We're asserting that comp is either a ContainerComponent or
// an InteractiveComponent. Neither would be a bug, therefore panic.
cc = &ActionRowComponent{comp.(InteractiveComponent)}
}
new[i] = cc
}
return new
}
// ComponentsPtr returns the pointer to Components' return. This is a
// convenient function.
func ComponentsPtr(components ...Component) *ContainerComponents {
v := Components(components...)
return &v
}
// Type implements the Component interface.
func (*ButtonComponent) Type() ComponentType {
return ButtonComponentType
func (a *ActionRowComponent) Type() ComponentType {
return ActionRowComponentType
}
// ButtonStyle is the style to display a button in.
type ButtonStyle uint
func (a *ActionRowComponent) _cmp() {}
func (a *ActionRowComponent) _ctn() {}
// All types of ButtonStyle documented.
const (
// PrimaryButton is a blurple button.
PrimaryButton ButtonStyle = iota + 1
// SecondaryButton is a grey button.
SecondaryButton
// SuccessButton is a green button.
SuccessButton
// DangerButton is a red button.
DangerButton
// LinkButton is a button that navigates to a URL.
LinkButton
)
// MarshalJSON marshals the action row in the format Discord expects.
func (a *ActionRowComponent) MarshalJSON() ([]byte, error) {
var actionRow struct {
Type ComponentType `json:"type"`
Components *[]InteractiveComponent `json:"components"`
}
// ButtonEmoji is the emoji displayed on the button before the text.
type ButtonEmoji struct {
Name string `json:"name,omitempty"`
actionRow.Components = (*[]InteractiveComponent)(a)
actionRow.Type = a.Type()
return json.Marshal(actionRow)
}
// UnmarshalJSON unmarshals JSON into the components. It does type-checking and
// will only accept interactive components.
func (a *ActionRowComponent) UnmarshalJSON(b []byte) error {
var row struct {
Components []json.Raw `json:"components"`
}
if err := json.Unmarshal(b, &row); err != nil {
return err
}
*a = make(ActionRowComponent, len(row.Components))
for i, b := range row.Components {
p, err := ParseComponent(b)
if err != nil {
return errors.Wrapf(err, "failed to parse component %d", i)
}
ic, ok := p.(InteractiveComponent)
if !ok {
return fmt.Errorf("expected interactive, got %T", p)
}
(*a)[i] = ic
}
return nil
}
// ComponentID is the type for a component's custom ID. It is NOT a snowflake,
// but rather a user-defined opaque string.
type ComponentID string
// ComponentEmoji is the emoji displayed on the button before the text. For more
// information, see Emoji.
type ComponentEmoji struct {
ID EmojiID `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Animated bool `json:"animated,omitempty"`
}
// MarshalJSON marshals the button in the format Discord expects.
func (b ButtonComponent) MarshalJSON() ([]byte, error) {
type button ButtonComponent
if b.Style == 0 {
b.Style = PrimaryButton // Sane default for button.
}
return json.Marshal(struct {
button
Type ComponentType `json:"type"`
}{
button: button(b),
Type: ButtonComponentType,
})
// ButtonComponentStyle is the style to display a button in. Use one of the
// ButtonStyle constructor functions.
type ButtonComponentStyle interface {
style() int
}
// SelectComponent is a clickable button that may be added to an interaction
type basicButtonStyle int
func (s basicButtonStyle) style() int { return int(s) }
const (
_ basicButtonStyle = iota
primaryButtonStyle
secondaryButtonStyle
successButtonStyle
dangerButtonStyle
linkButtonStyleNum
basicButtonStyleLen
)
// PrimaryButtonStyle is a style for a blurple button.
func PrimaryButtonStyle() ButtonComponentStyle { return primaryButtonStyle }
// SecondaryButtonStyle is a style for a grey button.
func SecondaryButtonStyle() ButtonComponentStyle { return secondaryButtonStyle }
// SuccessButtonStyle is a style for a green button.
func SuccessButtonStyle() ButtonComponentStyle { return successButtonStyle }
// DangerButtonStyle is a style for a red button.
func DangerButtonStyle() ButtonComponentStyle { return dangerButtonStyle }
type linkButtonStyle URL
func (s linkButtonStyle) style() int { return int(linkButtonStyleNum) }
// LinkButtonStyle is a button style that navigates to a URL.
func LinkButtonStyle(url URL) ButtonComponentStyle { return linkButtonStyle(url) }
// Button is a clickable button that may be added to an interaction
// response.
type SelectComponent struct {
CustomID string `json:"custom_id"`
Options []SelectComponentOption `json:"options"`
Placeholder string `json:"placeholder,omitempty"`
MinValues option.Int `json:"min_values,omitempty"`
MaxValues int `json:"max_values,omitempty"`
Disabled bool `json:"disabled,omitempty"`
type ButtonComponent struct {
// Style is one of the button styles.
Style ButtonComponentStyle `json:"style"`
// CustomID attached to InteractionCreate event when clicked.
CustomID ComponentID `json:"custom_id,omitempty"`
// Label is the text that appears on the button. It can have maximum 100
// characters.
Label string `json:"label,omitempty"`
// Emoji should have Name, ID and Animated filled.
Emoji *ComponentEmoji `json:"emoji,omitempty"`
// Disabled determines whether the button is disabled.
Disabled bool `json:"disabled,omitempty"`
}
type SelectComponentOption struct {
Label string `json:"label"`
Value string `json:"value"`
Description string `json:"description,omitempty"`
Emoji *ButtonEmoji `json:"emoji,omitempty"`
Default bool `json:"default,omitempty"`
// TextButtonComponent creates a new button with the given label used for the label and
// the custom ID.
func TextButtonComponent(style ButtonComponentStyle, label string) ButtonComponent {
return ButtonComponent{
Style: style,
Label: label,
CustomID: ComponentID(label),
}
}
// ID implements the Component interface.
func (b *ButtonComponent) ID() ComponentID { return b.CustomID }
// Type implements the Component interface.
func (*SelectComponent) Type() ComponentType {
func (b *ButtonComponent) Type() ComponentType {
return ButtonComponentType
}
func (b *ButtonComponent) _cmp() {}
func (b *ButtonComponent) _icp() {}
// MarshalJSON marshals the button in the format Discord expects.
func (b *ButtonComponent) MarshalJSON() ([]byte, error) {
if b.Style == nil {
b.Style = PrimaryButtonStyle() // Sane default for button.
}
type button ButtonComponent
type Msg struct {
*button
Type ComponentType `json:"type"`
Style int `json:"style"`
URL URL `json:"url,omitempty"`
}
msg := Msg{
Type: ButtonComponentType,
Style: b.Style.style(),
button: (*button)(b),
}
if link, ok := b.Style.(linkButtonStyle); ok {
msg.URL = URL(link)
}
return json.Marshal(msg)
}
// UnmarshalJSON unmarshals a component JSON into the button. It does NOT do
// type-checking; use ParseComponent for that.
func (b *ButtonComponent) UnmarshalJSON(j []byte) error {
type button ButtonComponent
msg := struct {
*button
Style basicButtonStyle `json:"style"`
URL URL `json:"url,omitempty"`
}{
button: (*button)(b),
}
if err := json.Unmarshal(j, &msg); err != nil {
return err
}
if 0 > msg.Style || msg.Style >= basicButtonStyleLen {
return fmt.Errorf("unknown button style %d", msg.Style)
}
switch msg.Style {
case linkButtonStyleNum:
b.Style = LinkButtonStyle(msg.URL)
default:
b.Style = msg.Style
}
return nil
}
// Select is a clickable button that may be added to an interaction
// response.
type SelectComponent struct {
// Options are the choices in the select.
Options []SelectOption `json:"options"`
// CustomID is the custom unique ID.
CustomID ComponentID `json:"custom_id,omitempty"`
// Placeholder is the custom placeholder text if nothing is selected. Max
// 100 characters.
Placeholder string `json:"placeholder,omitempty"`
// ValueLimits is the minimum and maximum number of items that can be
// chosen. The default is [1, 1] if ValueLimits is a zero-value.
ValueLimits [2]int `json:"-"`
// Disabled disables the select if true.
Disabled bool `json:"disabled,omitempty"`
}
// SelectOption is an option in the select component.
type SelectOption struct {
// Label is the user-facing name of the option. Max 100 characters.
Label string `json:"label"`
// Value is the internal value that is echoed back to the program. It's
// similar to the custom ID. Max 100 characters.
Value string `json:"value"`
// Description is the additional description of an option.
Description string `json:"description,omitempty"`
// Emoji is the optional emoji object.
Emoji *ComponentEmoji `json:"emoji,omitempty"`
// Default will render this option as selected by default if true.
Default bool `json:"default,omitempty"`
}
// ID implements the Component interface.
func (s *SelectComponent) ID() ComponentID { return s.CustomID }
// Type implements the Component interface.
func (s *SelectComponent) Type() ComponentType {
return SelectComponentType
}
// MarshalJSON marshals the select in the format Discord expects.
func (s SelectComponent) MarshalJSON() ([]byte, error) {
type selectComponent SelectComponent
func (s *SelectComponent) _cmp() {}
func (s *SelectComponent) _icp() {}
return json.Marshal(struct {
selectComponent
// MarshalJSON marshals the select in the format Discord expects.
func (s *SelectComponent) MarshalJSON() ([]byte, error) {
type sel SelectComponent
type Msg struct {
Type ComponentType `json:"type"`
}{
selectComponent: selectComponent(s),
Type: SelectComponentType,
})
*sel
MinValues *int `json:"min_values,omitempty"`
MaxValues *int `json:"max_values,omitempty"`
}
msg := Msg{
Type: SelectComponentType,
sel: (*sel)(s),
}
if s.ValueLimits != [2]int{0, 0} {
msg.MinValues = new(int)
msg.MaxValues = new(int)
*msg.MinValues = s.ValueLimits[0]
*msg.MaxValues = s.ValueLimits[1]
}
return json.Marshal(msg)
}
// UnknownComponent is reserved for components with unknown or not yet
// implemented components types.
// Unknown is reserved for components with unknown or not yet implemented
// components types. It can also be used in place of a ComponentInteraction.
type UnknownComponent struct {
json.Raw
id ComponentID
typ ComponentType
}
// Type implements the Component interface.
func (u *UnknownComponent) Type() ComponentType {
return u.typ
// ID implements the Component and ComponentInteraction interfaces.
func (u *UnknownComponent) ID() ComponentID { return u.id }
// Type implements the Component and ComponentInteraction interfaces.
func (u *UnknownComponent) Type() ComponentType { return u.typ }
// Type implements InteractionData.
func (u *UnknownComponent) InteractionType() InteractionDataType {
return ComponentInteractionType
}
func (u *UnknownComponent) resp() {}
func (u *UnknownComponent) data() {}
func (u *UnknownComponent) _cmp() {}
func (u *UnknownComponent) _icp() {}

View File

@ -4,14 +4,17 @@ import (
"strconv"
"github.com/diamondburned/arikawa/v3/utils/json"
"github.com/pkg/errors"
)
// InteractionEvent describes the full incoming interaction event. It may be a
// gateway event or a webhook event.
//
// https://discord.com/developers/docs/topics/gateway#interactions
type Interaction struct {
type InteractionEvent struct {
ID InteractionID `json:"id"`
Data InteractionData `json:"data"`
AppID AppID `json:"application_id"`
Type InteractionType `json:"type"`
Data InteractionData `json:"data,omitempty"`
ChannelID ChannelID `json:"channel_id,omitempty"`
Token string `json:"token"`
Version int `json:"version"`
@ -20,143 +23,315 @@ type Interaction struct {
// Only present for component interactions, not command interactions.
Message *Message `json:"message,omitempty"`
// Member is only present if this came from a guild.
// Member is only present if this came from a guild. To get a user, use the
// Sender method.
Member *Member `json:"member,omitempty"`
GuildID GuildID `json:"guild_id,omitempty"`
// User is only present if this didn't come from a guild.
// User is only present if this didn't come from a guild. To get a user, use
// the Sender method.
User *User `json:"user,omitempty"`
}
func (i *Interaction) UnmarshalJSON(p []byte) error {
type interaction Interaction
v := struct {
Data json.Raw `json:"data,omitempty"`
*interaction
}{interaction: (*interaction)(i)}
if err := json.Unmarshal(p, &v); err != nil {
// Sender returns the sender of this event from either the Member field or the
// User field. If neither of those fields are available, then nil is returned.
func (e *InteractionEvent) Sender() *User {
if e.User != nil {
return e.User
}
if e.Member != nil {
return &e.Member.User
}
return nil
}
// SenderID returns the sender's ID. See Sender for more information. If Sender
// returns nil, then 0 is returned.
func (e *InteractionEvent) SenderID() UserID {
if sender := e.Sender(); sender != nil {
return sender.ID
}
return 0
}
func (e *InteractionEvent) UnmarshalJSON(b []byte) error {
type event InteractionEvent
target := struct {
Type InteractionDataType `json:"type"`
Data json.Raw `json:"data"`
*event
}{
event: (*event)(e),
}
if err := json.Unmarshal(b, &target); err != nil {
return err
}
switch v.Type {
case PingInteraction:
var err error
switch target.Type {
case PingInteractionType:
e.Data = &PingInteraction{}
case CommandInteractionType:
e.Data = &CommandInteraction{}
case ComponentInteractionType:
d, err := ParseComponentInteraction(target.Data)
if err != nil {
return errors.Wrap(err, "failed to unmarshal component interaction event data")
}
e.Data = d
return nil
case ComponentInteraction:
i.Data = &ComponentInteractionData{}
case CommandInteraction:
i.Data = &CommandInteractionData{}
case AutocompleteInteraction:
i.Data = &AutocompleteInteractionData{}
case AutocompleteInteractionType:
e.Data = &AutocompleteInteraction{}
default:
i.Data = &UnknownInteractionData{typ: v.Type}
e.Data = &UnknownInteractionData{
Raw: target.Data,
typ: target.Type,
}
return nil
}
return json.Unmarshal(v.Data, i.Data)
if err := json.Unmarshal(target.Data, e.Data); err != nil {
return errors.Wrap(err, "failed to unmarshal interaction event data")
}
return err
}
type InteractionType uint
func (e *InteractionEvent) MarshalJSON() ([]byte, error) {
type event InteractionEvent
if e.Data == nil {
return nil, errors.New("missing InteractionEvent.Data")
}
if e.Data.InteractionType() == 0 {
return nil, errors.New("unexpected 0 InteractionEvent.Data.Type")
}
v := struct {
Type InteractionDataType `json:"type"`
*event
}{
Type: e.Data.InteractionType(),
event: (*event)(e),
}
return json.Marshal(v)
}
// InteractionDataType is the type of each Interaction, enumerated in
// integers.
type InteractionDataType uint
const (
PingInteraction InteractionType = iota + 1
CommandInteraction
ComponentInteraction
AutocompleteInteraction
PingInteractionType InteractionDataType = iota + 1
CommandInteractionType
ComponentInteractionType
AutocompleteInteractionType
)
// InteractionData holds the data of an interaction.
// Type assertions should be made on InteractionData to access the underlying data.
// The underlying types of the InteractionData are pointer types.
// InteractionData holds the respose data of an interaction, or more
// specifically, the data that Discord sends to us. Type assertions should be
// made on it to access the underlying data. The underlying types of the
// Responses are value types. See the constructors for the possible types.
type InteractionData interface {
Type() InteractionType
InteractionType() InteractionDataType
data()
}
type ComponentInteractionData struct {
CustomID string `json:"custom_id"`
ComponentType ComponentType `json:"component_type"`
Values []string `json:"values"`
}
// PingInteraction is a ping Interaction response.
type PingInteraction struct{}
func (*ComponentInteractionData) Type() InteractionType {
return ComponentInteraction
}
// InteractionType implements InteractionData.
func (*PingInteraction) InteractionType() InteractionDataType { return PingInteractionType }
func (*PingInteraction) data() {}
type CommandInteractionData struct {
ID CommandID `json:"id"`
Name string `json:"name"`
Options []InteractionOption `json:"options"`
}
func (*CommandInteractionData) Type() InteractionType {
return CommandInteraction
}
type AutocompleteInteractionData struct {
ID CommandID `json:"id"`
// AutocompleteInteraction is an autocompletion Interaction response.
type AutocompleteInteraction struct {
CommandID CommandID `json:"id"`
// Name of command autocomplete is triggered for.
Name string `json:"name"`
CommandType CommandType `json:"type"`
Version string `json:"version"`
Options []AutocompleteCommandOption `json:"options"`
Name string `json:"name"`
CommandType CommandType `json:"type"`
Version string `json:"version"`
Options []AutocompleteOption `json:"options"`
}
type AutocompleteCommandOption struct {
// AutocompleteOption is an autocompletion option in an AutocompleteInteraction.
type AutocompleteOption struct {
Type CommandOptionType `json:"type"`
Name string `json:"name"`
Value string `json:"value"`
Focused bool `json:"focused"`
}
func (*AutocompleteInteractionData) Type() InteractionType {
return AutocompleteInteraction
// Type implements ComponentInteraction.
func (*AutocompleteInteraction) InteractionType() InteractionDataType {
return AutocompleteInteractionType
}
func (*AutocompleteInteraction) data() {}
// ComponentInteraction is a union component interaction response types. The
// types can be whatever the constructors for this type will return. Underlying
// types of Response are all value types.
type ComponentInteraction interface {
InteractionData
// ID returns the ID of the component in response. Not all component
// interactions will have a component ID.
ID() ComponentID
// Type returns the type of the component in response.
Type() ComponentType
resp()
}
type UnknownInteractionData struct {
json.Raw
typ InteractionType
// SelectInteraction is a select component's response.
type SelectInteraction struct {
CustomID ComponentID `json:"custom_id"`
Values []string `json:"values"`
}
func (u *UnknownInteractionData) Type() InteractionType {
return u.typ
// ID implements ComponentInteraction.
func (s *SelectInteraction) ID() ComponentID { return s.CustomID }
// Type implements ComponentInteraction.
func (s *SelectInteraction) Type() ComponentType { return SelectComponentType }
// InteractionType implements InteractionData.
func (s *SelectInteraction) InteractionType() InteractionDataType {
return ComponentInteractionType
}
type InteractionOption struct {
Name string `json:"name"`
Value json.Raw `json:"value"`
Options []InteractionOption `json:"options"`
func (s *SelectInteraction) resp() {}
func (s *SelectInteraction) data() {}
// ButtonInteraction is a button component's response. It is the custom ID of
// the button within the component tree.
type ButtonInteraction struct {
CustomID ComponentID `json:"custom_id"`
}
// ID implements ComponentInteraction.
func (b *ButtonInteraction) ID() ComponentID { return b.CustomID }
// Type implements ComponentInteraction.
func (b *ButtonInteraction) Type() ComponentType { return ButtonComponentType }
// InteractionType implements InteractionData.
func (b *ButtonInteraction) InteractionType() InteractionDataType {
return ComponentInteractionType
}
func (b *ButtonInteraction) data() {}
func (b *ButtonInteraction) resp() {}
// ParseComponentInteraction parses the given bytes as a component response.
func ParseComponentInteraction(b []byte) (ComponentInteraction, error) {
var t struct {
Type ComponentType `json:"component_type"`
CustomID ComponentID `json:"custom_id"`
}
if err := json.Unmarshal(b, &t); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal component interaction header")
}
var d ComponentInteraction
switch t.Type {
case ButtonComponentType:
d = &ButtonInteraction{CustomID: t.CustomID}
case SelectComponentType:
d = &SelectInteraction{CustomID: t.CustomID}
default:
d = &UnknownComponent{
Raw: append(json.Raw(nil), b...),
id: t.CustomID,
typ: t.Type,
}
return d, nil
}
if err := json.Unmarshal(b, d); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal component interaction data")
}
return d, nil
}
// CommandInteraction is a command interaction that Discord sends to us.
type CommandInteraction struct {
ID CommandID `json:"id"`
Name string `json:"name"`
Options []CommandInteractionOption `json:"options"`
}
// InteractionType implements InteractionData.
func (*CommandInteraction) InteractionType() InteractionDataType {
return CommandInteractionType
}
func (*CommandInteraction) data() {}
// CommandInteractionOption is an option for a Command interaction response.
type CommandInteractionOption struct {
Name string `json:"name"`
Value json.Raw `json:"value"`
Options []CommandInteractionOption `json:"options"`
}
// String will return the value if the option's value is a valid string.
// Otherwise, it will return the raw JSON value of the other type.
func (o InteractionOption) String() string {
func (o CommandInteractionOption) String() string {
val := string(o.Value)
s, err := strconv.Unquote(val)
if err != nil {
return val
}
return s
}
func (o InteractionOption) Int() (int64, error) {
// IntValue reads the option's value as an int.
func (o CommandInteractionOption) IntValue() (int64, error) {
var i int64
err := o.Value.UnmarshalTo(&i)
return i, err
}
func (o InteractionOption) Bool() (bool, error) {
// BoolValue reads the option's value as a bool.
func (o CommandInteractionOption) BoolValue() (bool, error) {
var b bool
err := o.Value.UnmarshalTo(&b)
return b, err
}
func (o InteractionOption) Snowflake() (Snowflake, error) {
// SnowflakeValue reads the option's value as a snowflake.
func (o CommandInteractionOption) SnowflakeValue() (Snowflake, error) {
var id Snowflake
err := o.Value.UnmarshalTo(&id)
return id, err
}
func (o InteractionOption) Float() (float64, error) {
// FloatValue reads the option's value as a float64.
func (o CommandInteractionOption) FloatValue() (float64, error) {
var f float64
err := o.Value.UnmarshalTo(&f)
return f, err
}
// UnknownInteractionData describes an Interaction response with an unknown
// type.
type UnknownInteractionData struct {
json.Raw
typ InteractionDataType
}
// InteractionType implements InteractionData.
func (u *UnknownInteractionData) InteractionType() InteractionDataType {
return u.typ
}
func (u *UnknownInteractionData) data() {}

View File

@ -75,7 +75,7 @@ type Message struct {
// Reactions contains any reactions to the message.
Reactions []Reaction `json:"reactions,omitempty"`
// Components contains any attached components.
Components []ComponentWrap `json:"components,omitempty"`
Components ContainerComponents `json:"components,omitempty"`
// Used for validating a message was sent
Nonce string `json:"nonce,omitempty"`

View File

@ -15,16 +15,31 @@ func DurationSinceEpoch(t time.Time) time.Duration {
return time.Duration(t.UnixNano()) - Epoch
}
//go:generate go run ../utils/gensnowflake -o snowflake_types.go AppID AttachmentID AuditLogEntryID ChannelID CommandID EmojiID GuildID IntegrationID InteractionID MessageID RoleID StageID StickerID StickerPackID TeamID UserID WebhookID
// Mention generates the mention syntax for this channel ID.
func (s ChannelID) Mention() string { return "<#" + s.String() + ">" }
// Mention generates the mention syntax for this role ID.
func (s RoleID) Mention() string { return "<@&" + s.String() + ">" }
// Mention generates the mention syntax for this user ID.
func (s UserID) Mention() string { return "<@" + s.String() + ">" }
// Snowflake is the format of Discord's ID type. It is a format that can be
// sorted chronologically.
type Snowflake uint64
// NullSnowflake gets encoded into a null. This is used for
// optional and nullable snowflake fields.
const NullSnowflake = ^Snowflake(0)
// NewSnowflake creates a new snowflake from the given time.
func NewSnowflake(t time.Time) Snowflake {
return Snowflake((DurationSinceEpoch(t) / time.Millisecond) << 22)
}
// ParseSnowflake parses a snowflake.
func ParseSnowflake(sf string) (Snowflake, error) {
if sf == "null" {
return NullSnowflake, nil
@ -93,244 +108,3 @@ func (s Snowflake) PID() uint8 {
func (s Snowflake) Increment() uint16 {
return uint16(s & 0xFFF)
}
type AppID Snowflake
const NullAppID = AppID(NullSnowflake)
func (s AppID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AppID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s AppID) String() string { return Snowflake(s).String() }
func (s AppID) IsValid() bool { return Snowflake(s).IsValid() }
func (s AppID) IsNull() bool { return Snowflake(s).IsNull() }
func (s AppID) Time() time.Time { return Snowflake(s).Time() }
func (s AppID) Worker() uint8 { return Snowflake(s).Worker() }
func (s AppID) PID() uint8 { return Snowflake(s).PID() }
func (s AppID) Increment() uint16 { return Snowflake(s).Increment() }
type TeamID Snowflake
const NullTeamID = AppID(NullSnowflake)
func (s TeamID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *TeamID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s TeamID) String() string { return Snowflake(s).String() }
func (s TeamID) IsValid() bool { return Snowflake(s).IsValid() }
func (s TeamID) IsNull() bool { return Snowflake(s).IsNull() }
func (s TeamID) Time() time.Time { return Snowflake(s).Time() }
func (s TeamID) Worker() uint8 { return Snowflake(s).Worker() }
func (s TeamID) PID() uint8 { return Snowflake(s).PID() }
func (s TeamID) Increment() uint16 { return Snowflake(s).Increment() }
type AttachmentID Snowflake
const NullAttachmentID = AttachmentID(NullSnowflake)
func (s AttachmentID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AttachmentID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s AttachmentID) String() string { return Snowflake(s).String() }
func (s AttachmentID) IsValid() bool { return Snowflake(s).IsValid() }
func (s AttachmentID) IsNull() bool { return Snowflake(s).IsNull() }
func (s AttachmentID) Time() time.Time { return Snowflake(s).Time() }
func (s AttachmentID) Worker() uint8 { return Snowflake(s).Worker() }
func (s AttachmentID) PID() uint8 { return Snowflake(s).PID() }
func (s AttachmentID) Increment() uint16 { return Snowflake(s).Increment() }
type AuditLogEntryID Snowflake
const NullAuditLogEntryID = AuditLogEntryID(NullSnowflake)
func (s AuditLogEntryID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AuditLogEntryID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s AuditLogEntryID) String() string { return Snowflake(s).String() }
func (s AuditLogEntryID) IsValid() bool { return Snowflake(s).IsValid() }
func (s AuditLogEntryID) IsNull() bool { return Snowflake(s).IsNull() }
func (s AuditLogEntryID) Time() time.Time { return Snowflake(s).Time() }
func (s AuditLogEntryID) Worker() uint8 { return Snowflake(s).Worker() }
func (s AuditLogEntryID) PID() uint8 { return Snowflake(s).PID() }
func (s AuditLogEntryID) Increment() uint16 { return Snowflake(s).Increment() }
type ChannelID Snowflake
const NullChannelID = ChannelID(NullSnowflake)
func (s ChannelID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *ChannelID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s ChannelID) String() string { return Snowflake(s).String() }
func (s ChannelID) IsValid() bool { return Snowflake(s).IsValid() }
func (s ChannelID) IsNull() bool { return Snowflake(s).IsNull() }
func (s ChannelID) Time() time.Time { return Snowflake(s).Time() }
func (s ChannelID) Worker() uint8 { return Snowflake(s).Worker() }
func (s ChannelID) PID() uint8 { return Snowflake(s).PID() }
func (s ChannelID) Increment() uint16 { return Snowflake(s).Increment() }
func (s ChannelID) Mention() string { return "<#" + s.String() + ">" }
type CommandID Snowflake
const NullCommandID = CommandID(NullSnowflake)
func (s CommandID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *CommandID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s CommandID) String() string { return Snowflake(s).String() }
func (s CommandID) IsValid() bool { return Snowflake(s).IsValid() }
func (s CommandID) IsNull() bool { return Snowflake(s).IsNull() }
func (s CommandID) Time() time.Time { return Snowflake(s).Time() }
func (s CommandID) Worker() uint8 { return Snowflake(s).Worker() }
func (s CommandID) PID() uint8 { return Snowflake(s).PID() }
func (s CommandID) Increment() uint16 { return Snowflake(s).Increment() }
type EmojiID Snowflake
const NullEmojiID = EmojiID(NullSnowflake)
func (s EmojiID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *EmojiID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s EmojiID) String() string { return Snowflake(s).String() }
func (s EmojiID) IsValid() bool { return Snowflake(s).IsValid() }
func (s EmojiID) IsNull() bool { return Snowflake(s).IsNull() }
func (s EmojiID) Time() time.Time { return Snowflake(s).Time() }
func (s EmojiID) Worker() uint8 { return Snowflake(s).Worker() }
func (s EmojiID) PID() uint8 { return Snowflake(s).PID() }
func (s EmojiID) Increment() uint16 { return Snowflake(s).Increment() }
type IntegrationID Snowflake
const NullIntegrationID = IntegrationID(NullSnowflake)
func (s IntegrationID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *IntegrationID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s IntegrationID) String() string { return Snowflake(s).String() }
func (s IntegrationID) IsValid() bool { return Snowflake(s).IsValid() }
func (s IntegrationID) IsNull() bool { return Snowflake(s).IsNull() }
func (s IntegrationID) Time() time.Time { return Snowflake(s).Time() }
func (s IntegrationID) Worker() uint8 { return Snowflake(s).Worker() }
func (s IntegrationID) PID() uint8 { return Snowflake(s).PID() }
func (s IntegrationID) Increment() uint16 { return Snowflake(s).Increment() }
type InteractionID Snowflake
const NullInteractionID = InteractionID(NullSnowflake)
func (s InteractionID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *InteractionID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s InteractionID) String() string { return Snowflake(s).String() }
func (s InteractionID) IsValid() bool { return Snowflake(s).IsValid() }
func (s InteractionID) IsNull() bool { return Snowflake(s).IsNull() }
func (s InteractionID) Time() time.Time { return Snowflake(s).Time() }
func (s InteractionID) Worker() uint8 { return Snowflake(s).Worker() }
func (s InteractionID) PID() uint8 { return Snowflake(s).PID() }
func (s InteractionID) Increment() uint16 { return Snowflake(s).Increment() }
type GuildID Snowflake
const NullGuildID = GuildID(NullSnowflake)
func (s GuildID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *GuildID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s GuildID) String() string { return Snowflake(s).String() }
func (s GuildID) IsValid() bool { return Snowflake(s).IsValid() }
func (s GuildID) IsNull() bool { return Snowflake(s).IsNull() }
func (s GuildID) Time() time.Time { return Snowflake(s).Time() }
func (s GuildID) Worker() uint8 { return Snowflake(s).Worker() }
func (s GuildID) PID() uint8 { return Snowflake(s).PID() }
func (s GuildID) Increment() uint16 { return Snowflake(s).Increment() }
type MessageID Snowflake
const NullMessageID = MessageID(NullSnowflake)
func (s MessageID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *MessageID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s MessageID) String() string { return Snowflake(s).String() }
func (s MessageID) IsValid() bool { return Snowflake(s).IsValid() }
func (s MessageID) IsNull() bool { return Snowflake(s).IsNull() }
func (s MessageID) Time() time.Time { return Snowflake(s).Time() }
func (s MessageID) Worker() uint8 { return Snowflake(s).Worker() }
func (s MessageID) PID() uint8 { return Snowflake(s).PID() }
func (s MessageID) Increment() uint16 { return Snowflake(s).Increment() }
type RoleID Snowflake
const NullRoleID = RoleID(NullSnowflake)
func (s RoleID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *RoleID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s RoleID) String() string { return Snowflake(s).String() }
func (s RoleID) IsValid() bool { return Snowflake(s).IsValid() }
func (s RoleID) IsNull() bool { return Snowflake(s).IsNull() }
func (s RoleID) Time() time.Time { return Snowflake(s).Time() }
func (s RoleID) Worker() uint8 { return Snowflake(s).Worker() }
func (s RoleID) PID() uint8 { return Snowflake(s).PID() }
func (s RoleID) Increment() uint16 { return Snowflake(s).Increment() }
func (s RoleID) Mention() string { return "<@&" + s.String() + ">" }
type StageID Snowflake
const NullStageID = StageID(NullSnowflake)
func (s StageID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *StageID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s StageID) String() string { return Snowflake(s).String() }
func (s StageID) IsValid() bool { return Snowflake(s).IsValid() }
func (s StageID) IsNull() bool { return Snowflake(s).IsNull() }
func (s StageID) Time() time.Time { return Snowflake(s).Time() }
func (s StageID) Worker() uint8 { return Snowflake(s).Worker() }
func (s StageID) PID() uint8 { return Snowflake(s).PID() }
func (s StageID) Increment() uint16 { return Snowflake(s).Increment() }
type StickerID Snowflake
const NullStickerID = StickerID(NullSnowflake)
func (s StickerID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *StickerID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s StickerID) String() string { return Snowflake(s).String() }
func (s StickerID) IsValid() bool { return Snowflake(s).IsValid() }
func (s StickerID) IsNull() bool { return Snowflake(s).IsNull() }
func (s StickerID) Time() time.Time { return Snowflake(s).Time() }
func (s StickerID) Worker() uint8 { return Snowflake(s).Worker() }
func (s StickerID) PID() uint8 { return Snowflake(s).PID() }
func (s StickerID) Increment() uint16 { return Snowflake(s).Increment() }
type StickerPackID Snowflake
const NullStickerPackID = StickerPackID(NullSnowflake)
func (s StickerPackID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *StickerPackID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s StickerPackID) String() string { return Snowflake(s).String() }
func (s StickerPackID) IsValid() bool { return Snowflake(s).IsValid() }
func (s StickerPackID) IsNull() bool { return Snowflake(s).IsNull() }
func (s StickerPackID) Time() time.Time { return Snowflake(s).Time() }
func (s StickerPackID) Worker() uint8 { return Snowflake(s).Worker() }
func (s StickerPackID) PID() uint8 { return Snowflake(s).PID() }
func (s StickerPackID) Increment() uint16 { return Snowflake(s).Increment() }
type UserID Snowflake
const NullUserID = UserID(NullSnowflake)
func (s UserID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *UserID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s UserID) String() string { return Snowflake(s).String() }
func (s UserID) IsValid() bool { return Snowflake(s).IsValid() }
func (s UserID) IsNull() bool { return Snowflake(s).IsNull() }
func (s UserID) Time() time.Time { return Snowflake(s).Time() }
func (s UserID) Worker() uint8 { return Snowflake(s).Worker() }
func (s UserID) PID() uint8 { return Snowflake(s).PID() }
func (s UserID) Increment() uint16 { return Snowflake(s).Increment() }
func (s UserID) Mention() string { return "<@" + s.String() + ">" }
type WebhookID Snowflake
const NullWebhookID = WebhookID(NullSnowflake)
func (s WebhookID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *WebhookID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
func (s WebhookID) String() string { return Snowflake(s).String() }
func (s WebhookID) IsValid() bool { return Snowflake(s).IsValid() }
func (s WebhookID) IsNull() bool { return Snowflake(s).IsNull() }
func (s WebhookID) Time() time.Time { return Snowflake(s).Time() }
func (s WebhookID) Worker() uint8 { return Snowflake(s).Worker() }
func (s WebhookID) PID() uint8 { return Snowflake(s).PID() }
func (s WebhookID) Increment() uint16 { return Snowflake(s).Increment() }

396
discord/snowflake_types.go Normal file
View File

@ -0,0 +1,396 @@
// Code generated by gensnowflake. DO NOT EDIT.
package discord
import "time"
// AppID is the snowflake type for a AppID.
type AppID Snowflake
// NullAppID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullAppID = AppID(NullSnowflake)
func (s AppID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AppID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s AppID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s AppID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s AppID) IsNull() bool { return Snowflake(s).IsNull() }
func (s AppID) Time() time.Time { return Snowflake(s).Time() }
func (s AppID) Worker() uint8 { return Snowflake(s).Worker() }
func (s AppID) PID() uint8 { return Snowflake(s).PID() }
func (s AppID) Increment() uint16 { return Snowflake(s).Increment() }
// AttachmentID is the snowflake type for a AttachmentID.
type AttachmentID Snowflake
// NullAttachmentID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullAttachmentID = AttachmentID(NullSnowflake)
func (s AttachmentID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AttachmentID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s AttachmentID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s AttachmentID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s AttachmentID) IsNull() bool { return Snowflake(s).IsNull() }
func (s AttachmentID) Time() time.Time { return Snowflake(s).Time() }
func (s AttachmentID) Worker() uint8 { return Snowflake(s).Worker() }
func (s AttachmentID) PID() uint8 { return Snowflake(s).PID() }
func (s AttachmentID) Increment() uint16 { return Snowflake(s).Increment() }
// AuditLogEntryID is the snowflake type for a AuditLogEntryID.
type AuditLogEntryID Snowflake
// NullAuditLogEntryID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullAuditLogEntryID = AuditLogEntryID(NullSnowflake)
func (s AuditLogEntryID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *AuditLogEntryID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s AuditLogEntryID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s AuditLogEntryID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s AuditLogEntryID) IsNull() bool { return Snowflake(s).IsNull() }
func (s AuditLogEntryID) Time() time.Time { return Snowflake(s).Time() }
func (s AuditLogEntryID) Worker() uint8 { return Snowflake(s).Worker() }
func (s AuditLogEntryID) PID() uint8 { return Snowflake(s).PID() }
func (s AuditLogEntryID) Increment() uint16 { return Snowflake(s).Increment() }
// ChannelID is the snowflake type for a ChannelID.
type ChannelID Snowflake
// NullChannelID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullChannelID = ChannelID(NullSnowflake)
func (s ChannelID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *ChannelID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s ChannelID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s ChannelID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s ChannelID) IsNull() bool { return Snowflake(s).IsNull() }
func (s ChannelID) Time() time.Time { return Snowflake(s).Time() }
func (s ChannelID) Worker() uint8 { return Snowflake(s).Worker() }
func (s ChannelID) PID() uint8 { return Snowflake(s).PID() }
func (s ChannelID) Increment() uint16 { return Snowflake(s).Increment() }
// CommandID is the snowflake type for a CommandID.
type CommandID Snowflake
// NullCommandID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullCommandID = CommandID(NullSnowflake)
func (s CommandID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *CommandID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s CommandID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s CommandID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s CommandID) IsNull() bool { return Snowflake(s).IsNull() }
func (s CommandID) Time() time.Time { return Snowflake(s).Time() }
func (s CommandID) Worker() uint8 { return Snowflake(s).Worker() }
func (s CommandID) PID() uint8 { return Snowflake(s).PID() }
func (s CommandID) Increment() uint16 { return Snowflake(s).Increment() }
// EmojiID is the snowflake type for a EmojiID.
type EmojiID Snowflake
// NullEmojiID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullEmojiID = EmojiID(NullSnowflake)
func (s EmojiID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *EmojiID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s EmojiID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s EmojiID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s EmojiID) IsNull() bool { return Snowflake(s).IsNull() }
func (s EmojiID) Time() time.Time { return Snowflake(s).Time() }
func (s EmojiID) Worker() uint8 { return Snowflake(s).Worker() }
func (s EmojiID) PID() uint8 { return Snowflake(s).PID() }
func (s EmojiID) Increment() uint16 { return Snowflake(s).Increment() }
// GuildID is the snowflake type for a GuildID.
type GuildID Snowflake
// NullGuildID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullGuildID = GuildID(NullSnowflake)
func (s GuildID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *GuildID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s GuildID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s GuildID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s GuildID) IsNull() bool { return Snowflake(s).IsNull() }
func (s GuildID) Time() time.Time { return Snowflake(s).Time() }
func (s GuildID) Worker() uint8 { return Snowflake(s).Worker() }
func (s GuildID) PID() uint8 { return Snowflake(s).PID() }
func (s GuildID) Increment() uint16 { return Snowflake(s).Increment() }
// IntegrationID is the snowflake type for a IntegrationID.
type IntegrationID Snowflake
// NullIntegrationID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullIntegrationID = IntegrationID(NullSnowflake)
func (s IntegrationID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *IntegrationID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s IntegrationID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s IntegrationID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s IntegrationID) IsNull() bool { return Snowflake(s).IsNull() }
func (s IntegrationID) Time() time.Time { return Snowflake(s).Time() }
func (s IntegrationID) Worker() uint8 { return Snowflake(s).Worker() }
func (s IntegrationID) PID() uint8 { return Snowflake(s).PID() }
func (s IntegrationID) Increment() uint16 { return Snowflake(s).Increment() }
// InteractionID is the snowflake type for a InteractionID.
type InteractionID Snowflake
// NullInteractionID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullInteractionID = InteractionID(NullSnowflake)
func (s InteractionID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *InteractionID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s InteractionID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s InteractionID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s InteractionID) IsNull() bool { return Snowflake(s).IsNull() }
func (s InteractionID) Time() time.Time { return Snowflake(s).Time() }
func (s InteractionID) Worker() uint8 { return Snowflake(s).Worker() }
func (s InteractionID) PID() uint8 { return Snowflake(s).PID() }
func (s InteractionID) Increment() uint16 { return Snowflake(s).Increment() }
// MessageID is the snowflake type for a MessageID.
type MessageID Snowflake
// NullMessageID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullMessageID = MessageID(NullSnowflake)
func (s MessageID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *MessageID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s MessageID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s MessageID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s MessageID) IsNull() bool { return Snowflake(s).IsNull() }
func (s MessageID) Time() time.Time { return Snowflake(s).Time() }
func (s MessageID) Worker() uint8 { return Snowflake(s).Worker() }
func (s MessageID) PID() uint8 { return Snowflake(s).PID() }
func (s MessageID) Increment() uint16 { return Snowflake(s).Increment() }
// RoleID is the snowflake type for a RoleID.
type RoleID Snowflake
// NullRoleID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullRoleID = RoleID(NullSnowflake)
func (s RoleID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *RoleID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s RoleID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s RoleID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s RoleID) IsNull() bool { return Snowflake(s).IsNull() }
func (s RoleID) Time() time.Time { return Snowflake(s).Time() }
func (s RoleID) Worker() uint8 { return Snowflake(s).Worker() }
func (s RoleID) PID() uint8 { return Snowflake(s).PID() }
func (s RoleID) Increment() uint16 { return Snowflake(s).Increment() }
// StageID is the snowflake type for a StageID.
type StageID Snowflake
// NullStageID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullStageID = StageID(NullSnowflake)
func (s StageID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *StageID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s StageID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s StageID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s StageID) IsNull() bool { return Snowflake(s).IsNull() }
func (s StageID) Time() time.Time { return Snowflake(s).Time() }
func (s StageID) Worker() uint8 { return Snowflake(s).Worker() }
func (s StageID) PID() uint8 { return Snowflake(s).PID() }
func (s StageID) Increment() uint16 { return Snowflake(s).Increment() }
// StickerID is the snowflake type for a StickerID.
type StickerID Snowflake
// NullStickerID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullStickerID = StickerID(NullSnowflake)
func (s StickerID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *StickerID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s StickerID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s StickerID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s StickerID) IsNull() bool { return Snowflake(s).IsNull() }
func (s StickerID) Time() time.Time { return Snowflake(s).Time() }
func (s StickerID) Worker() uint8 { return Snowflake(s).Worker() }
func (s StickerID) PID() uint8 { return Snowflake(s).PID() }
func (s StickerID) Increment() uint16 { return Snowflake(s).Increment() }
// StickerPackID is the snowflake type for a StickerPackID.
type StickerPackID Snowflake
// NullStickerPackID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullStickerPackID = StickerPackID(NullSnowflake)
func (s StickerPackID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *StickerPackID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s StickerPackID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s StickerPackID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s StickerPackID) IsNull() bool { return Snowflake(s).IsNull() }
func (s StickerPackID) Time() time.Time { return Snowflake(s).Time() }
func (s StickerPackID) Worker() uint8 { return Snowflake(s).Worker() }
func (s StickerPackID) PID() uint8 { return Snowflake(s).PID() }
func (s StickerPackID) Increment() uint16 { return Snowflake(s).Increment() }
// TeamID is the snowflake type for a TeamID.
type TeamID Snowflake
// NullTeamID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullTeamID = TeamID(NullSnowflake)
func (s TeamID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *TeamID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s TeamID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s TeamID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s TeamID) IsNull() bool { return Snowflake(s).IsNull() }
func (s TeamID) Time() time.Time { return Snowflake(s).Time() }
func (s TeamID) Worker() uint8 { return Snowflake(s).Worker() }
func (s TeamID) PID() uint8 { return Snowflake(s).PID() }
func (s TeamID) Increment() uint16 { return Snowflake(s).Increment() }
// UserID is the snowflake type for a UserID.
type UserID Snowflake
// NullUserID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullUserID = UserID(NullSnowflake)
func (s UserID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *UserID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s UserID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s UserID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s UserID) IsNull() bool { return Snowflake(s).IsNull() }
func (s UserID) Time() time.Time { return Snowflake(s).Time() }
func (s UserID) Worker() uint8 { return Snowflake(s).Worker() }
func (s UserID) PID() uint8 { return Snowflake(s).PID() }
func (s UserID) Increment() uint16 { return Snowflake(s).Increment() }
// WebhookID is the snowflake type for a WebhookID.
type WebhookID Snowflake
// NullWebhookID gets encoded into a null. This is used for optional and nullable snowflake fields.
const NullWebhookID = WebhookID(NullSnowflake)
func (s WebhookID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() }
func (s *WebhookID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s WebhookID) String() string { return Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s WebhookID) IsValid() bool { return Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s WebhookID) IsNull() bool { return Snowflake(s).IsNull() }
func (s WebhookID) Time() time.Time { return Snowflake(s).Time() }
func (s WebhookID) Worker() uint8 { return Snowflake(s).Worker() }
func (s WebhookID) PID() uint8 { return Snowflake(s).PID() }
func (s WebhookID) Increment() uint16 { return Snowflake(s).Increment() }

View File

@ -397,7 +397,7 @@ type (
)
type InteractionCreateEvent struct {
discord.Interaction
discord.InteractionEvent
}
// Undocumented

View File

@ -193,6 +193,7 @@ func NewCustomIdentifiedGateway(gatewayURL string, id *Identifier) *Gateway {
ErrorLog: wsutil.WSError,
AfterClose: func(error) {},
PacerLoop: wsutil.PacemakerLoop{ErrorLog: wsutil.WSError},
}
}

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/diamondburned/arikawa/v3
go 1.13
go 1.16
require (
github.com/gorilla/schema v1.2.0

View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
types=(
SubcommandOption
SubcommandGroupOption
StringOption
IntegerOption
BooleanOption
UserOption
ChannelOption
RoleOption
MentionableOption
)
recvs=(
s
s
s
i
b
u
c
r
m
)
for ((i = 0; i < 6; i++)); {
cat<<EOF
// MarshalJSON marshals ${types[$i]} to JSON with the "type" field.
func (${recvs[$i]} *${types[$i]}) MarshalJSON() ([]byte, error) {
type raw ${types[$i]}
return json.Marshal(struct {
Type CommandOptionType \`json:"type"\`
*raw
}{
Type: ${recvs[$i]}.Type(),
raw: (*raw)(${recvs[$i]}),
})
}
EOF
}

View File

@ -0,0 +1,86 @@
package main
import (
"bytes"
"flag"
"go/format"
"log"
"os"
"path/filepath"
"text/template"
_ "embed"
)
type data struct {
Package string
ImportDiscord bool
Snowflakes []snowflakeType
}
type snowflakeType struct {
TypeName string
}
//go:embed template.tmpl
var packageTmpl string
var tmpl = template.Must(template.New("").Parse(packageTmpl))
func main() {
var pkg string
var out string
log.SetFlags(0)
flag.Usage = func() {
log.Printf("usage: %s [-p package] <type names...>", filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
flag.StringVar(&out, "o", "", "output, empty for stdout")
flag.StringVar(&pkg, "p", "discord", "package name")
flag.Parse()
if len(flag.Args()) == 0 {
flag.Usage()
os.Exit(1)
}
d := data{
Package: pkg,
ImportDiscord: pkg != "discord",
}
for _, arg := range flag.Args() {
d.Snowflakes = append(d.Snowflakes, snowflakeType{
TypeName: arg,
})
}
buf := bytes.Buffer{}
if err := tmpl.Execute(&buf, d); err != nil {
log.Fatalln("failed to execute template:", err)
}
b, err := format.Source(buf.Bytes())
if err != nil {
log.Fatalln("failed to fmt:", err)
}
outFile := os.Stdout
if out != "" {
f, err := os.Create(out)
if err != nil {
log.Fatalln("failed to create output file:", err)
}
defer f.Close()
outFile = f
}
if _, err := outFile.Write(b); err != nil {
log.Fatalln("failed to write to file:", err)
}
}

View File

@ -0,0 +1,38 @@
// Code generated by gensnowflake. DO NOT EDIT.
package {{ .Package }}
{{ $dot := "" }}
{{ if .ImportDiscord }}
{{ $dot = "discord." }}
import "github.com/diamondburned/arikawa/v3/discord"
{{ end }}
import "time"
{{ range .Snowflakes }}
// {{ .TypeName }} is the snowflake type for a {{ .TypeName }}.
type {{.TypeName}} {{$dot}}Snowflake
// Null{{.TypeName}} gets encoded into a null. This is used for optional and nullable snowflake fields.
const Null{{.TypeName}} = {{.TypeName}}({{$dot}}NullSnowflake)
func (s {{.TypeName}}) MarshalJSON() ([]byte, error) { return {{$dot}}Snowflake(s).MarshalJSON() }
func (s *{{.TypeName}}) UnmarshalJSON(v []byte) error { return (*{{$dot}}Snowflake)(s).UnmarshalJSON(v) }
// String returns the ID, or nothing if the snowflake isn't valid.
func (s {{.TypeName}}) String() string { return {{$dot}}Snowflake(s).String() }
// IsValid returns whether or not the snowflake is valid.
func (s {{.TypeName}}) IsValid() bool { return {{$dot}}Snowflake(s).IsValid() }
// IsNull returns whether or not the snowflake is null.
func (s {{.TypeName}}) IsNull() bool { return {{$dot}}Snowflake(s).IsNull() }
func (s {{.TypeName}}) Time() time.Time { return {{$dot}}Snowflake(s).Time() }
func (s {{.TypeName}}) Worker() uint8 { return {{$dot}}Snowflake(s).Worker() }
func (s {{.TypeName}}) PID() uint8 { return {{$dot}}Snowflake(s).PID() }
func (s {{.TypeName}}) Increment() uint16 { return {{$dot}}Snowflake(s).Increment() }
{{ end }}

View File

@ -267,7 +267,7 @@ func (c *Client) request(
}
// Optionally unmarshal the error.
json.Unmarshal(httpErr.Body, &httpErr)
json.Unmarshal(httpErr.Body, httpErr)
doErr = httpErr
}