2021-08-06 23:23:51 +00:00
package discord
import (
"encoding/json"
2021-08-21 16:15:27 +00:00
"fmt"
"time"
2021-08-06 23:23:51 +00:00
)
type Opcode int
// https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes
// NOTE(ben): I'm not using iota because 5 is missing
const (
OpcodeDispatch Opcode = 0
OpcodeHeartbeat Opcode = 1
OpcodeIdentify Opcode = 2
OpcodePresenceUpdate Opcode = 3
OpcodeVoiceStateUpdate Opcode = 4
OpcodeResume Opcode = 6
OpcodeReconnect Opcode = 7
OpcodeRequestGuildMembers Opcode = 8
OpcodeInvalidSession Opcode = 9
OpcodeHello Opcode = 10
OpcodeHeartbeatACK Opcode = 11
)
type Intent int
// https://discord.com/developers/docs/topics/gateway#list-of-intents
// NOTE(ben): I'm not using iota because the opcode thing made me paranoid
const (
IntentGuilds Intent = 1 << 0
IntentGuildMembers Intent = 1 << 1
IntentGuildBans Intent = 1 << 2
IntentGuildEmojisAndStickers Intent = 1 << 3
IntentGuildIntegrations Intent = 1 << 4
IntentGuildWebhooks Intent = 1 << 5
IntentGuildInvites Intent = 1 << 6
IntentGuildVoiceStates Intent = 1 << 7
IntentGuildPresences Intent = 1 << 8
IntentGuildMessages Intent = 1 << 9
IntentGuildMessageReactions Intent = 1 << 10
IntentGuildMessageTyping Intent = 1 << 11
IntentDirectMessages Intent = 1 << 12
IntentDirectMessageReactions Intent = 1 << 13
IntentDirectMessageTyping Intent = 1 << 14
)
type GatewayMessage struct {
Opcode Opcode ` json:"op" `
Data interface { } ` json:"d" `
SequenceNumber * int ` json:"s,omitempty" `
EventName * string ` json:"t,omitempty" `
}
func ( m * GatewayMessage ) ToJSON ( ) [ ] byte {
mBytes , err := json . Marshal ( m )
if err != nil {
panic ( err )
}
return mBytes
}
type Hello struct {
HeartbeatIntervalMs int ` json:"heartbeat_interval" `
}
func HelloFromMap ( m interface { } ) Hello {
return Hello {
HeartbeatIntervalMs : int ( m . ( map [ string ] interface { } ) [ "heartbeat_interval" ] . ( float64 ) ) ,
}
}
type Identify struct {
Token string ` json:"token" `
Properties IdentifyConnectionProperties ` json:"properties" `
Intents Intent ` json:"intents" `
}
type IdentifyConnectionProperties struct {
OS string ` json:"$os" `
Browser string ` json:"$browser" `
Device string ` json:"$device" `
}
type Ready struct {
GatewayVersion int ` json:"v" `
User User ` json:"user" `
SessionID string ` json:"session_id" `
}
func ReadyFromMap ( m interface { } ) Ready {
mmap := m . ( map [ string ] interface { } )
return Ready {
GatewayVersion : int ( mmap [ "v" ] . ( float64 ) ) ,
2021-09-26 22:34:38 +00:00
User : * UserFromMap ( mmap [ "user" ] , "" ) ,
2021-08-06 23:23:51 +00:00
SessionID : mmap [ "session_id" ] . ( string ) ,
}
}
type Resume struct {
Token string ` json:"token" `
SessionID string ` json:"session_id" `
SequenceNumber int ` json:"seq" `
}
2021-08-26 23:33:39 +00:00
// https://discord.com/developers/docs/topics/gateway#message-delete
type MessageDelete struct {
ID string ` json:"id" `
ChannelID string ` json:"channel_id" `
GuildID string ` json:"guild_id" `
}
func MessageDeleteFromMap ( m interface { } ) MessageDelete {
mmap := m . ( map [ string ] interface { } )
return MessageDelete {
ID : mmap [ "id" ] . ( string ) ,
ChannelID : mmap [ "channel_id" ] . ( string ) ,
2021-08-27 00:58:41 +00:00
GuildID : maybeString ( mmap , "guild_id" ) ,
}
}
// https://discord.com/developers/docs/topics/gateway#message-delete
type MessageBulkDelete struct {
IDs [ ] string ` json:"ids" `
ChannelID string ` json:"channel_id" `
GuildID string ` json:"guild_id" `
}
func MessageBulkDeleteFromMap ( m interface { } ) MessageBulkDelete {
mmap := m . ( map [ string ] interface { } )
iids := mmap [ "ids" ] . ( [ ] interface { } )
ids := make ( [ ] string , len ( iids ) )
for i , iid := range iids {
ids [ i ] = iid . ( string )
}
return MessageBulkDelete {
IDs : ids ,
ChannelID : mmap [ "channel_id" ] . ( string ) ,
2021-08-26 23:33:39 +00:00
GuildID : maybeString ( mmap , "guild_id" ) ,
}
}
2021-08-06 23:23:51 +00:00
type ChannelType int
2021-08-23 21:52:57 +00:00
// https://discord.com/developers/docs/resources/channel#channel-object-channel-types
2021-08-06 23:23:51 +00:00
const (
2021-08-23 21:52:57 +00:00
ChannelTypeGuildText ChannelType = 0
2021-08-06 23:23:51 +00:00
ChannelTypeDM ChannelType = 1
ChannelTypeGuildVoice ChannelType = 2
ChannelTypeGroupDM ChannelType = 3
ChannelTypeGuildCategory ChannelType = 4
ChannelTypeGuildNews ChannelType = 5
ChannelTypeGuildStore ChannelType = 6
ChannelTypeGuildNewsThread ChannelType = 10
ChannelTypeGuildPublicThread ChannelType = 11
ChannelTypeGuildPrivateThread ChannelType = 12
ChannelTypeGuildStageVoice ChannelType = 13
)
2021-08-24 03:26:27 +00:00
// https://discord.com/developers/docs/topics/permissions#role-object
type Role struct {
ID string ` json:"id" `
Name string ` json:"name" `
// more fields not yet present
}
2021-09-26 22:34:38 +00:00
func RoleFromMap ( m interface { } , k string ) * Role {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
r := & Role {
ID : mmap [ "id" ] . ( string ) ,
Name : mmap [ "name" ] . ( string ) ,
}
return r
}
2021-08-23 21:52:57 +00:00
// https://discord.com/developers/docs/resources/channel#channel-object
2021-08-06 23:23:51 +00:00
type Channel struct {
2021-09-26 22:34:38 +00:00
ID string ` json:"id" `
Type ChannelType ` json:"type" `
GuildID string ` json:"guild_id" `
Name string ` json:"name" `
// More fields not yet present
}
func ChannelFromMap ( m interface { } , k string ) * Channel {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
c := & Channel {
ID : mmap [ "id" ] . ( string ) ,
Type : ChannelType ( mmap [ "type" ] . ( float64 ) ) ,
GuildID : maybeString ( mmap , "guild_id" ) ,
Name : maybeString ( mmap , "name" ) ,
}
return c
2021-08-06 23:23:51 +00:00
}
type MessageType int
2021-08-23 21:52:57 +00:00
// https://discord.com/developers/docs/resources/channel#message-object-message-types
2021-08-06 23:23:51 +00:00
const (
MessageTypeDefault MessageType = 0
MessageTypeRecipientAdd MessageType = 1
MessageTypeRecipientRemove MessageType = 2
MessageTypeCall MessageType = 3
MessageTypeChannelNameChange MessageType = 4
MessageTypeChannelIconChange MessageType = 5
MessageTypeChannelPinnedMessage MessageType = 6
MessageTypeGuildMemberJoin MessageType = 7
MessageTypeUserPremiumGuildSubscription MessageType = 8
MessageTypeUserPremiumGuildSubscriptionTier1 MessageType = 9
MessageTypeUserPremiumGuildSubscriptionTier2 MessageType = 10
MessageTypeUserPremiumGuildSubscriptionTier3 MessageType = 11
MessageTypeChannelFollowAdd MessageType = 12
MessageTypeGuildDiscoveryDisqualified MessageType = 14
MessageTypeGuildDiscoveryRequalified MessageType = 15
MessageTypeGuildDiscoveryGracePeriodInitialWarning MessageType = 16
MessageTypeGuildDiscoveryGracePeriodFinalWarning MessageType = 17
MessageTypeThreadCreated MessageType = 18
MessageTypeReply MessageType = 19
MessageTypeApplicationCommand MessageType = 20
MessageTypeThreadStarterMessage MessageType = 21
MessageTypeGuildInviteReminder MessageType = 22
)
// https://discord.com/developers/docs/resources/channel#message-object
type Message struct {
2021-08-21 16:15:27 +00:00
ID string ` json:"id" `
ChannelID string ` json:"channel_id" `
GuildID * string ` json:"guild_id" `
Content string ` json:"content" `
2021-09-26 22:34:38 +00:00
Author * User ` json:"author" ` // note that this may not be an actual valid user (see the docs)
2021-08-21 16:15:27 +00:00
Timestamp string ` json:"timestamp" `
Type MessageType ` json:"type" `
2021-08-06 23:23:51 +00:00
Attachments [ ] Attachment ` json:"attachments" `
2021-08-23 21:52:57 +00:00
Embeds [ ] Embed ` json:"embeds" `
2021-08-06 23:23:51 +00:00
originalMap map [ string ] interface { }
}
2021-08-21 16:15:27 +00:00
func ( m * Message ) JumpURL ( ) string {
guildStr := "@me"
if m . GuildID != nil {
guildStr = * m . GuildID
}
return fmt . Sprintf ( "https://discord.com/channels/%s/%s/%s" , guildStr , m . ChannelID , m . ID )
}
func ( m * Message ) Time ( ) time . Time {
t , err := time . Parse ( time . RFC3339Nano , m . Timestamp )
if err != nil {
panic ( err )
}
return t
}
2021-08-27 03:59:12 +00:00
func ( m * Message ) ShortString ( ) string {
return fmt . Sprintf ( "%s / %s: \"%s\" (%d attachments, %d embeds)" , m . Timestamp , m . Author . Username , m . Content , len ( m . Attachments ) , len ( m . Embeds ) )
}
2021-08-21 16:15:27 +00:00
func ( m * Message ) OriginalHasFields ( fields ... string ) bool {
if m . originalMap == nil {
2021-08-27 03:59:12 +00:00
// If we don't know, we assume the fields are there.
// Usually this is because it came from their API, where we
// always have all fields.
return true
2021-08-21 16:15:27 +00:00
}
for _ , field := range fields {
_ , ok := m . originalMap [ field ]
if ! ok {
return false
}
}
return true
}
2021-09-26 22:34:38 +00:00
func MessageFromMap ( m interface { } , k string ) * Message {
2021-08-06 23:23:51 +00:00
/ *
Some gateway events , like MESSAGE_UPDATE , do not contain the
entire message body . So we need to be defensive on all fields here ,
except the most basic identifying information .
* /
2021-09-26 22:34:38 +00:00
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
2021-08-06 23:23:51 +00:00
msg := Message {
ID : mmap [ "id" ] . ( string ) ,
ChannelID : mmap [ "channel_id" ] . ( string ) ,
2021-08-21 16:15:27 +00:00
GuildID : maybeStringP ( mmap , "guild_id" ) ,
2021-08-06 23:23:51 +00:00
Content : maybeString ( mmap , "content" ) ,
2021-09-26 22:34:38 +00:00
Author : UserFromMap ( m , "author" ) ,
2021-08-21 16:15:27 +00:00
Timestamp : maybeString ( mmap , "timestamp" ) ,
2021-08-06 23:23:51 +00:00
Type : MessageType ( maybeInt ( mmap , "type" ) ) ,
originalMap : mmap ,
}
if iattachments , ok := mmap [ "attachments" ] ; ok {
attachments := iattachments . ( [ ] interface { } )
for _ , iattachment := range attachments {
2021-09-26 22:34:38 +00:00
msg . Attachments = append ( msg . Attachments , * AttachmentFromMap ( iattachment , "" ) )
2021-08-06 23:23:51 +00:00
}
}
2021-08-23 21:52:57 +00:00
if iembeds , ok := mmap [ "embeds" ] ; ok {
embeds := iembeds . ( [ ] interface { } )
for _ , iembed := range embeds {
2021-09-26 22:34:38 +00:00
msg . Embeds = append ( msg . Embeds , * EmbedFromMap ( iembed ) )
2021-08-23 21:52:57 +00:00
}
}
2021-08-21 16:15:27 +00:00
2021-09-26 22:34:38 +00:00
return & msg
2021-08-06 23:23:51 +00:00
}
// https://discord.com/developers/docs/resources/user#user-object
type User struct {
2021-08-16 04:40:56 +00:00
ID string ` json:"id" `
Username string ` json:"username" `
Discriminator string ` json:"discriminator" `
Avatar * string ` json:"avatar" `
IsBot bool ` json:"bot" `
Locale string ` json:"locale" `
2021-08-06 23:23:51 +00:00
}
2021-09-26 22:34:38 +00:00
func UserFromMap ( m interface { } , k string ) * User {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
2021-08-06 23:23:51 +00:00
u := User {
ID : mmap [ "id" ] . ( string ) ,
Username : mmap [ "username" ] . ( string ) ,
Discriminator : mmap [ "discriminator" ] . ( string ) ,
}
if isBot , ok := mmap [ "bot" ] ; ok {
u . IsBot = isBot . ( bool )
}
2021-09-26 22:34:38 +00:00
return & u
}
type Guild struct {
ID string ` json:"id" `
// Who cares about the rest tbh
}
func GuildFromMap ( m interface { } , k string ) * Guild {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
g := Guild {
ID : mmap [ "id" ] . ( string ) ,
}
return & g
2021-08-06 23:23:51 +00:00
}
2021-08-24 03:26:27 +00:00
// https://discord.com/developers/docs/resources/guild#guild-member-object
type GuildMember struct {
User * User ` json:"user" `
Nick * string ` json:"nick" `
// more fields not yet handled here
}
2021-09-26 22:34:38 +00:00
func ( gm * GuildMember ) DisplayName ( ) string {
if gm . Nick != nil {
return * gm . Nick
}
if gm . User != nil {
return gm . User . Username
}
return "<UNKNOWN USER>"
}
func GuildMemberFromMap ( m interface { } , k string ) * GuildMember {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
gm := & GuildMember {
User : UserFromMap ( m , "user" ) ,
Nick : maybeStringP ( mmap , "nick" ) ,
}
return gm
}
2021-08-23 21:52:57 +00:00
// https://discord.com/developers/docs/resources/channel#attachment-object
2021-08-06 23:23:51 +00:00
type Attachment struct {
2021-08-23 21:52:57 +00:00
ID string ` json:"id" `
Filename string ` json:"filename" `
ContentType * string ` json:"content_type" `
Size int ` json:"size" `
Url string ` json:"url" `
ProxyUrl string ` json:"proxy_url" `
Height * int ` json:"height" `
Width * int ` json:"width" `
2021-08-06 23:23:51 +00:00
}
2021-09-26 22:34:38 +00:00
func AttachmentFromMap ( m interface { } , k string ) * Attachment {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
2021-08-06 23:23:51 +00:00
a := Attachment {
ID : mmap [ "id" ] . ( string ) ,
Filename : mmap [ "filename" ] . ( string ) ,
2021-08-23 21:52:57 +00:00
ContentType : maybeStringP ( mmap , "content_type" ) ,
2021-08-06 23:23:51 +00:00
Size : int ( mmap [ "size" ] . ( float64 ) ) ,
Url : mmap [ "url" ] . ( string ) ,
ProxyUrl : mmap [ "proxy_url" ] . ( string ) ,
Height : maybeIntP ( mmap , "height" ) ,
Width : maybeIntP ( mmap , "width" ) ,
}
2021-09-26 22:34:38 +00:00
return & a
2021-08-06 23:23:51 +00:00
}
2021-08-23 21:52:57 +00:00
// https://discord.com/developers/docs/resources/channel#embed-object
type Embed struct {
Title * string ` json:"title" `
Type * string ` json:"type" `
Description * string ` json:"description" `
Url * string ` json:"url" `
Timestamp * string ` json:"timestamp" `
Color * int ` json:"color" `
Footer * EmbedFooter ` json:"footer" `
Image * EmbedImage ` json:"image" `
Thumbnail * EmbedThumbnail ` json:"thumbnail" `
Video * EmbedVideo ` json:"video" `
Provider * EmbedProvider ` json:"provider" `
Author * EmbedAuthor ` json:"author" `
Fields [ ] EmbedField ` json:"fields" `
}
type EmbedFooter struct {
Text string ` json:"text" `
IconUrl * string ` json:"icon_url" `
ProxyIconUrl * string ` json:"proxy_icon_url" `
}
type EmbedImageish struct {
Url * string ` json:"url" `
ProxyUrl * string ` json:"proxy_url" `
Height * int ` json:"height" `
Width * int ` json:"width" `
}
type EmbedImage struct {
EmbedImageish
}
type EmbedThumbnail struct {
EmbedImageish
}
type EmbedVideo struct {
EmbedImageish
}
type EmbedProvider struct {
Name * string ` json:"name" `
Url * string ` json:"url" `
}
type EmbedAuthor struct {
Name * string ` json:"name" `
Url * string ` json:"url" `
IconUrl * string ` json:"icon_url" `
ProxyIconUrl * string ` json:"proxy_icon_url" `
}
type EmbedField struct {
Name string ` json:"name" `
Value string ` json:"value" `
Inline * bool ` json:"inline" `
}
2021-09-26 22:34:38 +00:00
func EmbedFromMap ( m interface { } ) * Embed {
2021-08-23 21:52:57 +00:00
mmap := m . ( map [ string ] interface { } )
2021-09-26 22:34:38 +00:00
if mmap == nil {
return nil
}
2021-08-23 21:52:57 +00:00
e := Embed {
Title : maybeStringP ( mmap , "title" ) ,
Type : maybeStringP ( mmap , "type" ) ,
Description : maybeStringP ( mmap , "description" ) ,
Url : maybeStringP ( mmap , "url" ) ,
Timestamp : maybeStringP ( mmap , "timestamp" ) ,
Color : maybeIntP ( mmap , "color" ) ,
Footer : EmbedFooterFromMap ( mmap , "footer" ) ,
Image : EmbedImageFromMap ( mmap , "image" ) ,
Thumbnail : EmbedThumbnailFromMap ( mmap , "thumbnail" ) ,
Video : EmbedVideoFromMap ( mmap , "video" ) ,
Provider : EmbedProviderFromMap ( mmap , "provider" ) ,
Author : EmbedAuthorFromMap ( mmap , "author" ) ,
Fields : EmbedFieldsFromMap ( mmap , "fields" ) ,
}
2021-09-26 22:34:38 +00:00
return & e
2021-08-23 21:52:57 +00:00
}
func EmbedFooterFromMap ( m map [ string ] interface { } , k string ) * EmbedFooter {
f , ok := m [ k ]
if ! ok {
return nil
}
fMap , ok := f . ( map [ string ] interface { } )
if ! ok {
return nil
}
return & EmbedFooter {
Text : maybeString ( fMap , "text" ) ,
IconUrl : maybeStringP ( fMap , "icon_url" ) ,
ProxyIconUrl : maybeStringP ( fMap , "proxy_icon_url" ) ,
}
}
func EmbedImageFromMap ( m map [ string ] interface { } , k string ) * EmbedImage {
val , ok := m [ k ]
if ! ok {
return nil
}
valMap , ok := val . ( map [ string ] interface { } )
if ! ok {
return nil
}
return & EmbedImage {
EmbedImageish : EmbedImageish {
Url : maybeStringP ( valMap , "url" ) ,
ProxyUrl : maybeStringP ( valMap , "proxy_url" ) ,
Height : maybeIntP ( valMap , "height" ) ,
Width : maybeIntP ( valMap , "width" ) ,
} ,
}
}
func EmbedThumbnailFromMap ( m map [ string ] interface { } , k string ) * EmbedThumbnail {
val , ok := m [ k ]
if ! ok {
return nil
}
valMap , ok := val . ( map [ string ] interface { } )
if ! ok {
return nil
}
return & EmbedThumbnail {
EmbedImageish : EmbedImageish {
Url : maybeStringP ( valMap , "url" ) ,
ProxyUrl : maybeStringP ( valMap , "proxy_url" ) ,
Height : maybeIntP ( valMap , "height" ) ,
Width : maybeIntP ( valMap , "width" ) ,
} ,
}
}
func EmbedVideoFromMap ( m map [ string ] interface { } , k string ) * EmbedVideo {
val , ok := m [ k ]
if ! ok {
return nil
}
valMap , ok := val . ( map [ string ] interface { } )
if ! ok {
return nil
}
return & EmbedVideo {
EmbedImageish : EmbedImageish {
Url : maybeStringP ( valMap , "url" ) ,
ProxyUrl : maybeStringP ( valMap , "proxy_url" ) ,
Height : maybeIntP ( valMap , "height" ) ,
Width : maybeIntP ( valMap , "width" ) ,
} ,
}
}
func EmbedProviderFromMap ( m map [ string ] interface { } , k string ) * EmbedProvider {
val , ok := m [ k ]
if ! ok {
return nil
}
valMap , ok := val . ( map [ string ] interface { } )
if ! ok {
return nil
}
return & EmbedProvider {
Name : maybeStringP ( valMap , "name" ) ,
Url : maybeStringP ( valMap , "url" ) ,
}
}
func EmbedAuthorFromMap ( m map [ string ] interface { } , k string ) * EmbedAuthor {
val , ok := m [ k ]
if ! ok {
return nil
}
valMap , ok := val . ( map [ string ] interface { } )
if ! ok {
return nil
}
return & EmbedAuthor {
Name : maybeStringP ( valMap , "name" ) ,
Url : maybeStringP ( valMap , "url" ) ,
}
}
func EmbedFieldsFromMap ( m map [ string ] interface { } , k string ) [ ] EmbedField {
val , ok := m [ k ]
if ! ok {
return nil
}
valSlice , ok := val . ( [ ] interface { } )
if ! ok {
return nil
}
var result [ ] EmbedField
for _ , innerVal := range valSlice {
valMap , ok := innerVal . ( map [ string ] interface { } )
if ! ok {
continue
}
result = append ( result , EmbedField {
Name : maybeString ( valMap , "name" ) ,
Value : maybeString ( valMap , "value" ) ,
Inline : maybeBoolP ( valMap , "inline" ) ,
} )
}
return result
}
2021-09-26 22:34:38 +00:00
// Data is always present on application command and message component interaction types. It is optional for future-proofing against new interaction types.
//
// Member is sent when the interaction is invoked in a guild, and User is sent when invoked in a DM.
//
// See https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure
type Interaction struct {
ID string ` json:"id" ` // id of the interaction
ApplicationID string ` json:"application_id" ` // id of the application this interaction is for
Type InteractionType ` json:"type" ` // the type of interaction
Data * InteractionData ` json:"data" ` // the command data payload
GuildID string ` json:"guild_id" ` // the guild it was sent from
ChannelID string ` json:"channel_id" ` // the channel it was sent from
Member * GuildMember ` json:"member" ` // guild member data for the invoking user, including permissions
User * User ` json:"user" ` // user object for the invoking user, if invoked in a DM
Token string ` json:"token" ` // a continuation token for responding to the interaction
Version int ` json:"version" ` // read-only property, always 1
Message * Message ` json:"message" ` // for components, the message they were attached to
}
// https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type
type InteractionType int
const (
InteractionTypePing InteractionType = 1
InteractionTypeApplicationCommand InteractionType = 2
InteractionTypeMessageComponent InteractionType = 3
)
// See https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-data-structure
type InteractionData struct {
// Fields for Application Commands
// See https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-structure
ID string ` json:"id" ` // the ID of the invoked command
Name string ` json:"name" ` // the name of the invoked command
Type ApplicationCommandType ` json:"type" ` // the type of the invoked command
Resolved * ResolvedData ` json:"resolved" ` // converted users + roles + channels
Options [ ] ApplicationCommandInteractionDataOption ` json:"options" ` // the params + values from the user
// Fields for Components
// TODO
// Fields for User Command and Message Command
2021-09-27 01:30:09 +00:00
TargetID string ` json:"target_id" ` // id the of user or message targetted by a user or message command
2021-09-26 22:34:38 +00:00
}
// See https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure
type ResolvedData struct {
Users map [ string ] User ` json:"users" `
Members map [ string ] GuildMember ` json:"members" ` // Partial Member objects are missing user, deaf and mute fields. If data for a Member is included, data for its corresponding User will also be included.
Roles map [ string ] Role ` json:"roles" `
Channels map [ string ] Channel ` json:"channels" ` // Partial Channel objects only have id, name, type and permissions fields. Threads will also have thread_metadata and parent_id fields.
Messages map [ string ] Message ` json:"messages" `
}
// See https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-response-structure
type InteractionResponse struct {
Type InteractionCallbackType ` json:"type" ` // the type of response
Data * InteractionCallbackData ` json:"data" ` // an optional response message
}
// See https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type
type InteractionCallbackType int
const (
InteractionCallbackTypePong InteractionCallbackType = 1 // ACK a Ping
InteractionCallbackTypeChannelMessageWithSource InteractionCallbackType = 4 // respond to an interaction with a message
InteractionCallbackTypeDeferredChannelMessageWithSource InteractionCallbackType = 5 // ACK an interaction and edit a response later, the user sees a loading state
InteractionCallbackTypeDeferredUpdateMessage InteractionCallbackType = 6 // for components, ACK an interaction and edit the original message later; the user does not see a loading state
InteractionCallbackTypeUpdateMessage InteractionCallbackType = 7 // for components, edit the message the component was attached to
)
type InteractionCallbackData struct {
TTS bool ` json:"bool,omitempty" `
Content string ` json:"content,omitempty" `
Embeds [ ] Embed ` json:"embeds,omitempty" `
// TODO: Allowed mentions
Flags InteractionCallbackDataFlags ` json:"flags,omitempty" `
// TODO: Components
}
type InteractionCallbackDataFlags int
const (
FlagEphemeral InteractionCallbackDataFlags = 1 << 6
)
type ApplicationCommandType int
const (
ApplicationCommandTypeChatInput ApplicationCommandType = 1 // Slash commands; a text-based command that shows up when a user types `/`
ApplicationCommandTypeUser ApplicationCommandType = 2 // A UI-based command that shows up when you right click or tap on a user
ApplicationCommandTypeMessage ApplicationCommandOptionType = 3 // A UI-based command that shows up when you right click or tap on a message
)
// Required `options` must be listed before optional options
type ApplicationCommandOption struct {
Type ApplicationCommandOptionType ` json:"type" ` // the type of option
Name string ` json:"name" ` // 1-32 character name
Description string ` json:"description" ` // 1-100 character description
Required bool ` json:"required" ` // if the parameter is required or optional--default false
Choices [ ] ApplicationCommandOptionChoice ` json:"choices" ` // choices for STRING, INTEGER, and NUMBER types for the user to pick from, max 25
Options [ ] ApplicationCommandOption ` json:"options" ` // if the option is a subcommand or subcommand group type, this nested options will be the parameters
}
type ApplicationCommandOptionType int
const (
ApplicationCommandOptionTypeSubCommand ApplicationCommandOptionType = 1
ApplicationCommandOptionTypeSubCommandGroup ApplicationCommandOptionType = 2
ApplicationCommandOptionTypeString ApplicationCommandOptionType = 3
ApplicationCommandOptionTypeInteger ApplicationCommandOptionType = 4 // Any integer between -2^53 and 2^53
ApplicationCommandOptionTypeBoolean ApplicationCommandOptionType = 5
ApplicationCommandOptionTypeUser ApplicationCommandOptionType = 6
ApplicationCommandOptionTypeChannel ApplicationCommandOptionType = 7 // Includes all channel types + categories
ApplicationCommandOptionTypeRole ApplicationCommandOptionType = 8
ApplicationCommandOptionTypeMentionable ApplicationCommandOptionType = 9 // Includes users and roles
ApplicationCommandOptionTypeNumber ApplicationCommandOptionType = 10 // Any double between -2^53 and 2^53
)
// If you specify `choices` for an option, they are the only valid values for a user to pick
type ApplicationCommandOptionChoice struct {
Name string ` json:"name" ` // 1-100 character choice name
Value interface { } ` json:"value" ` // value of the choice, up to 100 characters if string
}
// All options have names, and an option can either be a parameter and input
// value--in which case Value will be set--or it can denote a subcommand or
// group--in which case it will contain a top-level key and another array
// of Options.
//
// Value and Options are mutually exclusive.
type ApplicationCommandInteractionDataOption struct {
Name string ` json:"name" `
Type ApplicationCommandOptionType ` json:"type" `
Value interface { } ` json:"value" ` // the value of the pair
Options [ ] ApplicationCommandInteractionDataOption ` json:"options" ` // present if this option is a group or subcommand
}
func InteractionFromMap ( m interface { } , k string ) * Interaction {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
i := & Interaction {
ID : mmap [ "id" ] . ( string ) ,
ApplicationID : mmap [ "application_id" ] . ( string ) ,
Type : InteractionType ( mmap [ "type" ] . ( float64 ) ) ,
Data : InteractionDataFromMap ( m , "data" ) ,
GuildID : maybeString ( mmap , "guild_id" ) ,
ChannelID : maybeString ( mmap , "channel_id" ) ,
Member : GuildMemberFromMap ( mmap , "member" ) ,
User : UserFromMap ( mmap , "user" ) ,
Token : mmap [ "token" ] . ( string ) ,
Version : int ( mmap [ "version" ] . ( float64 ) ) ,
Message : MessageFromMap ( mmap , "message" ) ,
}
return i
}
func InteractionDataFromMap ( m interface { } , k string ) * InteractionData {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
d := & InteractionData {
ID : mmap [ "id" ] . ( string ) ,
Name : mmap [ "name" ] . ( string ) ,
Type : ApplicationCommandType ( mmap [ "type" ] . ( float64 ) ) ,
Resolved : ResolvedDataFromMap ( mmap , "resolved" ) ,
2021-09-27 01:30:09 +00:00
TargetID : maybeString ( mmap , "target_id" ) ,
2021-09-26 22:34:38 +00:00
}
if ioptions , ok := mmap [ "options" ] ; ok {
options := ioptions . ( [ ] interface { } )
for _ , ioption := range options {
d . Options = append ( d . Options , * ApplicationCommandInteractionDataOptionFromMap ( ioption , "" ) )
}
}
return d
}
func ResolvedDataFromMap ( m interface { } , k string ) * ResolvedData {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
d := & ResolvedData { }
if iusers , ok := mmap [ "users" ] ; ok {
users := iusers . ( map [ string ] interface { } )
d . Users = make ( map [ string ] User )
for id , iuser := range users {
d . Users [ id ] = * UserFromMap ( iuser , "" )
}
}
if imembers , ok := mmap [ "members" ] ; ok {
members := imembers . ( map [ string ] interface { } )
d . Members = make ( map [ string ] GuildMember )
for id , imember := range members {
member := * GuildMemberFromMap ( imember , "" )
user := d . Users [ id ]
member . User = & user
d . Members [ id ] = member
}
}
if iroles , ok := mmap [ "roles" ] ; ok {
roles := iroles . ( map [ string ] interface { } )
d . Roles = make ( map [ string ] Role )
for id , irole := range roles {
d . Roles [ id ] = * RoleFromMap ( irole , "" )
}
}
if ichannels , ok := mmap [ "channels" ] ; ok {
channels := ichannels . ( map [ string ] interface { } )
d . Channels = make ( map [ string ] Channel )
for id , ichannel := range channels {
d . Channels [ id ] = * ChannelFromMap ( ichannel , "" )
}
}
if imessages , ok := mmap [ "messages" ] ; ok {
messages := imessages . ( map [ string ] interface { } )
d . Messages = make ( map [ string ] Message )
for id , imessage := range messages {
d . Messages [ id ] = * MessageFromMap ( imessage , "" )
}
}
return d
}
func ApplicationCommandInteractionDataOptionFromMap ( m interface { } , k string ) * ApplicationCommandInteractionDataOption {
mmap := maybeGetKey ( m , k )
if mmap == nil {
return nil
}
o := & ApplicationCommandInteractionDataOption {
Name : mmap [ "name" ] . ( string ) ,
Type : ApplicationCommandOptionType ( mmap [ "type" ] . ( float64 ) ) ,
Value : mmap [ "value" ] ,
}
if ioptions , ok := mmap [ "options" ] ; ok {
options := ioptions . ( [ ] interface { } )
for _ , ioption := range options {
o . Options = append ( o . Options , * ApplicationCommandInteractionDataOptionFromMap ( ioption , "" ) )
}
}
return o
}
// If called without a key, returns m. Otherwise, returns m[k].
// If m[k] does not exist, returns nil.
//
// The intent is to allow the ThingFromMap functions to be flexibly called,
// either with the data in question as the root (no key) or as a child of
// another object (with a key).
func maybeGetKey ( m interface { } , k string ) map [ string ] interface { } {
if k == "" {
return m . ( map [ string ] interface { } )
} else {
mmap := m . ( map [ string ] interface { } )
if mk , ok := mmap [ k ] ; ok {
return mk . ( map [ string ] interface { } )
} else {
return nil
}
}
}
2021-08-06 23:23:51 +00:00
func maybeString ( m map [ string ] interface { } , k string ) string {
val , ok := m [ k ]
if ! ok {
return ""
}
return val . ( string )
}
2021-08-21 16:15:27 +00:00
func maybeStringP ( m map [ string ] interface { } , k string ) * string {
val , ok := m [ k ]
2021-09-26 22:34:38 +00:00
if ! ok || val == nil {
2021-08-21 16:15:27 +00:00
return nil
}
strval := val . ( string )
return & strval
}
2021-08-06 23:23:51 +00:00
func maybeInt ( m map [ string ] interface { } , k string ) int {
val , ok := m [ k ]
if ! ok {
return 0
}
return int ( val . ( float64 ) )
}
func maybeIntP ( m map [ string ] interface { } , k string ) * int {
val , ok := m [ k ]
2021-09-26 22:34:38 +00:00
if ! ok || val == nil {
2021-08-06 23:23:51 +00:00
return nil
}
intval := int ( val . ( float64 ) )
return & intval
}
2021-08-23 21:52:57 +00:00
func maybeBool ( m map [ string ] interface { } , k string ) bool {
val , ok := m [ k ]
if ! ok {
return false
}
return val . ( bool )
}
func maybeBoolP ( m map [ string ] interface { } , k string ) * bool {
val , ok := m [ k ]
2021-09-26 22:34:38 +00:00
if ! ok || val == nil {
2021-08-23 21:52:57 +00:00
return nil
}
boolval := val . ( bool )
return & boolval
}