mirror of
https://github.com/diamondburned/arikawa.git
synced 2024-11-01 04:24:19 +00:00
d8438f3b51
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.
338 lines
9.4 KiB
Go
338 lines
9.4 KiB
Go
package discord
|
|
|
|
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 InteractionEvent struct {
|
|
ID InteractionID `json:"id"`
|
|
Data InteractionData `json:"data"`
|
|
AppID AppID `json:"application_id"`
|
|
ChannelID ChannelID `json:"channel_id,omitempty"`
|
|
Token string `json:"token"`
|
|
Version int `json:"version"`
|
|
|
|
// Message is the message the component was attached to.
|
|
// Only present for component interactions, not command interactions.
|
|
Message *Message `json:"message,omitempty"`
|
|
|
|
// 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. To get a user, use
|
|
// the Sender method.
|
|
User *User `json:"user,omitempty"`
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
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 AutocompleteInteractionType:
|
|
e.Data = &AutocompleteInteraction{}
|
|
default:
|
|
e.Data = &UnknownInteractionData{
|
|
Raw: target.Data,
|
|
typ: target.Type,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if err := json.Unmarshal(target.Data, e.Data); err != nil {
|
|
return errors.Wrap(err, "failed to unmarshal interaction event data")
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
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 (
|
|
PingInteractionType InteractionDataType = iota + 1
|
|
CommandInteractionType
|
|
ComponentInteractionType
|
|
AutocompleteInteractionType
|
|
)
|
|
|
|
// 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 {
|
|
InteractionType() InteractionDataType
|
|
data()
|
|
}
|
|
|
|
// PingInteraction is a ping Interaction response.
|
|
type PingInteraction struct{}
|
|
|
|
// InteractionType implements InteractionData.
|
|
func (*PingInteraction) InteractionType() InteractionDataType { return PingInteractionType }
|
|
func (*PingInteraction) data() {}
|
|
|
|
// 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 []AutocompleteOption `json:"options"`
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
|
|
// SelectInteraction is a select component's response.
|
|
type SelectInteraction struct {
|
|
CustomID ComponentID `json:"custom_id"`
|
|
Values []string `json:"values"`
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
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 CommandInteractionOption) String() string {
|
|
val := string(o.Value)
|
|
|
|
s, err := strconv.Unquote(val)
|
|
if err != nil {
|
|
return val
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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() {}
|