1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2024-09-17 15:38:46 +00:00

discord: Add new select components

This commit is contained in:
twoscott 2022-11-19 17:47:08 +00:00 committed by diamondburned
parent e5aabda660
commit 1b31249626
3 changed files with 253 additions and 29 deletions

View file

@ -8,7 +8,6 @@ import (
"github.com/diamondburned/arikawa/v3/internal/rfutil"
"github.com/diamondburned/arikawa/v3/utils/json"
"github.com/diamondburned/arikawa/v3/utils/json/option"
"github.com/pkg/errors"
)
@ -19,8 +18,12 @@ const (
_ ComponentType = iota
ActionRowComponentType
ButtonComponentType
SelectComponentType
StringSelectComponentType
TextInputComponentType
UserSelectComponentType
RoleSelectComponentType
MentionableSelectComponentType
ChannelSelectComponentType
)
// String formats Type's name as a string.
@ -30,10 +33,18 @@ func (t ComponentType) String() string {
return "ActionRow"
case ButtonComponentType:
return "Button"
case SelectComponentType:
return "Select"
case StringSelectComponentType:
return "StringSelect"
case TextInputComponentType:
return "TextInput"
case UserSelectComponentType:
return "User"
case RoleSelectComponentType:
return "Role"
case MentionableSelectComponentType:
return "Mentionable"
case ChannelSelectComponentType:
return "Channel"
default:
return fmt.Sprintf("ComponentType(%d)", int(t))
}
@ -142,8 +153,8 @@ func (c *ContainerComponents) Unmarshal(v interface{}) error {
switch component := component.(type) {
case *TextInputComponent:
v = component.Value.Val
case *SelectComponent:
v = component.Value
case *StringSelectComponent:
switch len(component.Options) {
case 0:
// ok
@ -186,7 +197,7 @@ func (c *ContainerComponents) Unmarshal(v interface{}) error {
switch elemt.Kind() {
case reflect.String:
switch component := component.(type) {
case *SelectComponent:
case *StringSelectComponent:
fieldv.Set(reflect.MakeSlice(fieldt, len(component.Options), len(component.Options)))
for i, option := range component.Options {
fieldv.Index(i).Set(reflect.ValueOf(option.Value).Convert(elemt))
@ -241,6 +252,10 @@ func (c *ContainerComponents) UnmarshalJSON(b []byte) error {
// - *ButtonComponent
// - *SelectComponent
// - *TextInputComponent
// - *UserSelectComponent
// - *RoleSelectComponent
// - *MentionableSelectComponent
// - *ChannelSelectComponent
//
type Component interface {
// Type returns the type of the underlying component.
@ -257,6 +272,10 @@ type Component interface {
// - *ButtonComponent
// - *SelectComponent
// - *TextInputComponent
// - *UserSelectComponent
// - *RoleSelectComponent
// - *MentionableSelectComponent
// - *ChannelSelectComponent
//
type InteractiveComponent interface {
Component
@ -296,8 +315,8 @@ func ParseComponent(b []byte) (Component, error) {
c = &ActionRowComponent{}
case ButtonComponentType:
c = &ButtonComponent{}
case SelectComponentType:
c = &SelectComponent{}
case StringSelectComponentType:
c = &StringSelectComponent{}
case TextInputComponentType:
c = &TextInputComponent{}
default:
@ -562,9 +581,9 @@ func (b *ButtonComponent) UnmarshalJSON(j []byte) error {
return nil
}
// Select is a clickable button that may be added to an interaction
// StringSelectComponent is a clickable button that may be added to an interaction
// response.
type SelectComponent struct {
type StringSelectComponent struct {
// Options are the choices in the select.
Options []SelectOption `json:"options"`
// CustomID is the custom unique ID.
@ -595,19 +614,19 @@ type SelectOption struct {
}
// ID implements the Component interface.
func (s *SelectComponent) ID() ComponentID { return s.CustomID }
func (s *StringSelectComponent) ID() ComponentID { return s.CustomID }
// Type implements the Component interface.
func (s *SelectComponent) Type() ComponentType {
return SelectComponentType
func (s *StringSelectComponent) Type() ComponentType {
return StringSelectComponentType
}
func (s *SelectComponent) _cmp() {}
func (s *SelectComponent) _icp() {}
func (s *StringSelectComponent) _cmp() {}
func (s *StringSelectComponent) _icp() {}
// MarshalJSON marshals the select in the format Discord expects.
func (s *SelectComponent) MarshalJSON() ([]byte, error) {
type sel SelectComponent
func (s *StringSelectComponent) MarshalJSON() ([]byte, error) {
type sel StringSelectComponent
type Msg struct {
Type ComponentType `json:"type"`
@ -617,7 +636,7 @@ func (s *SelectComponent) MarshalJSON() ([]byte, error) {
}
msg := Msg{
Type: SelectComponentType,
Type: StringSelectComponentType,
sel: (*sel)(s),
}
@ -654,9 +673,9 @@ type TextInputComponent struct {
// Required dictates whether or not the user must fill out the component
Required bool `json:"required"`
// Value is the pre-filled value of this component (max 4000 chars)
Value option.NullableString `json:"value,omitempty"`
Value string `json:"value,omitempty"`
// Placeholder is the text that appears when the input is empty (max 100 chars)
Placeholder option.NullableString `json:"placeholder,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
}
func (s *TextInputComponent) _cmp() {}
@ -695,6 +714,212 @@ func (i *TextInputComponent) MarshalJSON() ([]byte, error) {
return json.Marshal(m)
}
type UserSelectComponent struct {
// 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"`
}
// ID implements the Component interface.
func (s *UserSelectComponent) ID() ComponentID { return s.CustomID }
// Type implements the Component interface.
func (s *UserSelectComponent) Type() ComponentType {
return UserSelectComponentType
}
func (s *UserSelectComponent) _cmp() {}
func (s *UserSelectComponent) _icp() {}
// MarshalJSON marshals the select in the format Discord expects.
func (s *UserSelectComponent) MarshalJSON() ([]byte, error) {
type sel UserSelectComponent
type Msg struct {
Type ComponentType `json:"type"`
*sel
MinValues *int `json:"min_values,omitempty"`
MaxValues *int `json:"max_values,omitempty"`
}
msg := Msg{
Type: UserSelectComponentType,
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)
}
type RoleSelectComponent struct {
// 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"`
}
// ID implements the Component interface.
func (s *RoleSelectComponent) ID() ComponentID { return s.CustomID }
// Type implements the Component interface.
func (s *RoleSelectComponent) Type() ComponentType {
return RoleSelectComponentType
}
func (s *RoleSelectComponent) _cmp() {}
func (s *RoleSelectComponent) _icp() {}
// MarshalJSON marshals the select in the format Discord expects.
func (s *RoleSelectComponent) MarshalJSON() ([]byte, error) {
type sel RoleSelectComponent
type Msg struct {
Type ComponentType `json:"type"`
*sel
MinValues *int `json:"min_values,omitempty"`
MaxValues *int `json:"max_values,omitempty"`
}
msg := Msg{
Type: RoleSelectComponentType,
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)
}
type MentionableSelectComponent struct {
// 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"`
}
// ID implements the Component interface.
func (s *MentionableSelectComponent) ID() ComponentID { return s.CustomID }
// Type implements the Component interface.
func (s *MentionableSelectComponent) Type() ComponentType {
return MentionableSelectComponentType
}
func (s *MentionableSelectComponent) _cmp() {}
func (s *MentionableSelectComponent) _icp() {}
// MarshalJSON marshals the select in the format Discord expects.
func (s *MentionableSelectComponent) MarshalJSON() ([]byte, error) {
type sel MentionableSelectComponent
type Msg struct {
Type ComponentType `json:"type"`
*sel
MinValues *int `json:"min_values,omitempty"`
MaxValues *int `json:"max_values,omitempty"`
}
msg := Msg{
Type: MentionableSelectComponentType,
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)
}
type ChannelSelectComponent struct {
// 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"`
// ChannelTypes is the types of channels that can be chosen from.
ChannelTypes []ChannelType `json:"channel_types,omitempty"`
}
// ID implements the Component interface.
func (s *ChannelSelectComponent) ID() ComponentID { return s.CustomID }
// Type implements the Component interface.
func (s *ChannelSelectComponent) Type() ComponentType {
return ChannelSelectComponentType
}
func (s *ChannelSelectComponent) _cmp() {}
func (s *ChannelSelectComponent) _icp() {}
// MarshalJSON marshals the select in the format Discord expects.
func (s *ChannelSelectComponent) MarshalJSON() ([]byte, error) {
type sel ChannelSelectComponent
type Msg struct {
Type ComponentType `json:"type"`
*sel
MinValues *int `json:"min_values,omitempty"`
MaxValues *int `json:"max_values,omitempty"`
}
msg := Msg{
Type: ChannelSelectComponentType,
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)
}
// 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 {

View file

@ -6,7 +6,6 @@ import (
"log"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/utils/json/option"
)
func ExampleContainerComponents_Unmarshal() {
@ -14,21 +13,21 @@ func ExampleContainerComponents_Unmarshal() {
&discord.ActionRowComponent{
&discord.TextInputComponent{
CustomID: "text1",
Value: option.NewNullableString("hello"),
Value: "hello",
},
},
&discord.ActionRowComponent{
&discord.TextInputComponent{
CustomID: "text2",
Value: option.NewNullableString("hello 2"),
Value: "hello 2",
},
&discord.TextInputComponent{
CustomID: "text3",
Value: option.NewNullableString("hello 3"),
Value: "hello 3",
},
},
&discord.ActionRowComponent{
&discord.SelectComponent{
&discord.StringSelectComponent{
CustomID: "select1",
Options: []discord.SelectOption{
{Value: "option 1"},
@ -40,7 +39,7 @@ func ExampleContainerComponents_Unmarshal() {
},
},
&discord.ActionRowComponent{
&discord.SelectComponent{
&discord.StringSelectComponent{
CustomID: "select2",
Options: []discord.SelectOption{
{Value: "option 1"},

View file

@ -305,7 +305,7 @@ type SelectInteraction struct {
func (s *SelectInteraction) ID() ComponentID { return s.CustomID }
// Type implements ComponentInteraction.
func (s *SelectInteraction) Type() ComponentType { return SelectComponentType }
func (s *SelectInteraction) Type() ComponentType { return StringSelectComponentType }
// InteractionType implements InteractionData.
func (s *SelectInteraction) InteractionType() InteractionDataType {
@ -351,7 +351,7 @@ func ParseComponentInteraction(b []byte) (ComponentInteraction, error) {
switch t.Type {
case ButtonComponentType:
d = &ButtonInteraction{CustomID: t.CustomID}
case SelectComponentType:
case StringSelectComponentType:
d = &SelectInteraction{CustomID: t.CustomID}
default:
d = &UnknownComponent{