diff --git a/endpoints.go b/endpoints.go index b2e632692..29189af69 100644 --- a/endpoints.go +++ b/endpoints.go @@ -162,6 +162,16 @@ var ( return EndpointMessageReactions(cID, mID, eID) + "/" + uID } + EndpointPoll = func(cID, mID string) string { + return EndpointChannel(cID) + "/polls/" + mID + } + EndpointPollAnswerVoters = func(cID, mID string, aID int) string { + return EndpointPoll(cID, mID) + "/answers/" + strconv.Itoa(aID) + } + EndpointPollExpire = func(cID, mID string) string { + return EndpointPoll(cID, mID) + "/expire" + } + EndpointApplicationGlobalCommands = func(aID string) string { return EndpointApplication(aID) + "/commands" } diff --git a/eventhandlers.go b/eventhandlers.go index fc0071736..1d0a61638 100644 --- a/eventhandlers.go +++ b/eventhandlers.go @@ -45,6 +45,8 @@ const ( messageCreateEventType = "MESSAGE_CREATE" messageDeleteEventType = "MESSAGE_DELETE" messageDeleteBulkEventType = "MESSAGE_DELETE_BULK" + messagePollVoteAddEventType = "MESSAGE_POLL_VOTE_ADD" + messagePollVoteRemoveEventType = "MESSAGE_POLL_VOTE_REMOVE" messageReactionAddEventType = "MESSAGE_REACTION_ADD" messageReactionRemoveEventType = "MESSAGE_REACTION_REMOVE" messageReactionRemoveAllEventType = "MESSAGE_REACTION_REMOVE_ALL" @@ -815,6 +817,46 @@ func (eh messageDeleteBulkEventHandler) Handle(s *Session, i interface{}) { } } +// messagePollVoteAddEventHandler is an event handler for MessagePollVoteAdd events. +type messagePollVoteAddEventHandler func(*Session, *MessagePollVoteAdd) + +// Type returns the event type for MessagePollVoteAdd events. +func (eh messagePollVoteAddEventHandler) Type() string { + return messagePollVoteAddEventType +} + +// New returns a new instance of MessagePollVoteAdd. +func (eh messagePollVoteAddEventHandler) New() interface{} { + return &MessagePollVoteAdd{} +} + +// Handle is the handler for MessagePollVoteAdd events. +func (eh messagePollVoteAddEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*MessagePollVoteAdd); ok { + eh(s, t) + } +} + +// messagePollVoteRemoveEventHandler is an event handler for MessagePollVoteRemove events. +type messagePollVoteRemoveEventHandler func(*Session, *MessagePollVoteRemove) + +// Type returns the event type for MessagePollVoteRemove events. +func (eh messagePollVoteRemoveEventHandler) Type() string { + return messagePollVoteRemoveEventType +} + +// New returns a new instance of MessagePollVoteRemove. +func (eh messagePollVoteRemoveEventHandler) New() interface{} { + return &MessagePollVoteRemove{} +} + +// Handle is the handler for MessagePollVoteRemove events. +func (eh messagePollVoteRemoveEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*MessagePollVoteRemove); ok { + eh(s, t) + } +} + // messageReactionAddEventHandler is an event handler for MessageReactionAdd events. type messageReactionAddEventHandler func(*Session, *MessageReactionAdd) @@ -1350,6 +1392,10 @@ func handlerForInterface(handler interface{}) EventHandler { return messageDeleteEventHandler(v) case func(*Session, *MessageDeleteBulk): return messageDeleteBulkEventHandler(v) + case func(*Session, *MessagePollVoteAdd): + return messagePollVoteAddEventHandler(v) + case func(*Session, *MessagePollVoteRemove): + return messagePollVoteRemoveEventHandler(v) case func(*Session, *MessageReactionAdd): return messageReactionAddEventHandler(v) case func(*Session, *MessageReactionRemove): @@ -1437,6 +1483,8 @@ func init() { registerInterfaceProvider(messageCreateEventHandler(nil)) registerInterfaceProvider(messageDeleteEventHandler(nil)) registerInterfaceProvider(messageDeleteBulkEventHandler(nil)) + registerInterfaceProvider(messagePollVoteAddEventHandler(nil)) + registerInterfaceProvider(messagePollVoteRemoveEventHandler(nil)) registerInterfaceProvider(messageReactionAddEventHandler(nil)) registerInterfaceProvider(messageReactionRemoveEventHandler(nil)) registerInterfaceProvider(messageReactionRemoveAllEventHandler(nil)) diff --git a/events.go b/events.go index 8438ff833..d36b97608 100644 --- a/events.go +++ b/events.go @@ -406,3 +406,21 @@ type AutoModerationActionExecution struct { type GuildAuditLogEntryCreate struct { *AuditLogEntry } + +// MessagePollVoteAdd is the data for a MessagePollVoteAdd event. +type MessagePollVoteAdd struct { + UserID string `json:"user_id"` + ChannelID string `json:"channel_id"` + MessageID string `json:"message_id"` + GuildID string `json:"guild_id,omitempty"` + AnswerID int `json:"answer_id"` +} + +// MessagePollVoteRemove is the data for a MessagePollVoteRemove event. +type MessagePollVoteRemove struct { + UserID string `json:"user_id"` + ChannelID string `json:"channel_id"` + MessageID string `json:"message_id"` + GuildID string `json:"guild_id,omitempty"` + AnswerID int `json:"answer_id"` +} diff --git a/interactions.go b/interactions.go index c562463af..247a02144 100644 --- a/interactions.go +++ b/interactions.go @@ -555,6 +555,7 @@ type InteractionResponseData struct { AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` Files []*File `json:"-"` Attachments *[]*MessageAttachment `json:"attachments,omitempty"` + Poll *Poll `json:"poll,omitempty"` // NOTE: only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set. Flags MessageFlags `json:"flags,omitempty"` diff --git a/message.go b/message.go index 3b03ed5f9..08bc5e0f6 100644 --- a/message.go +++ b/message.go @@ -239,6 +239,7 @@ type MessageSend struct { Reference *MessageReference `json:"message_reference,omitempty"` StickerIDs []string `json:"sticker_ids"` Flags MessageFlags `json:"flags,omitempty"` + Poll *Poll `json:"poll,omitempty"` // TODO: Remove this when compatibility is not required. File *File `json:"-"` diff --git a/restapi.go b/restapi.go index 97d1b588a..cc9e3649b 100644 --- a/restapi.go +++ b/restapi.go @@ -3453,3 +3453,49 @@ func (s *Session) UserApplicationRoleConnectionUpdate(appID string, rconn *Appli err = unmarshal(body, &st) return } + +// ---------------------------------------------------------------------- +// Functions specific to polls +// ---------------------------------------------------------------------- + +// PollAnswerVoters returns users who voted for a particular answer in a poll on the specified message. +// channelID : ID of the channel. +// messageID : ID of the message. +// answerID : ID of the answer. +func (s *Session) PollAnswerVoters(channelID, messageID string, answerID int) (voters []*User, err error) { + endpoint := EndpointPollAnswerVoters(channelID, messageID, answerID) + + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + var r struct { + Users []*User `json:"users"` + } + + err = unmarshal(body, &r) + if err != nil { + return + } + + voters = r.Users + return +} + +// PollExpire expires poll on the specified message. +// channelID : ID of the channel. +// messageID : ID of the message. +func (s *Session) PollExpire(channelID, messageID string) (msg *Message, err error) { + endpoint := EndpointPollExpire(channelID, messageID) + + var body []byte + body, err = s.RequestWithBucketID("POST", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &msg) + return +} diff --git a/structs.go b/structs.go index 4df8bd5e4..a5e9db91a 100644 --- a/structs.go +++ b/structs.go @@ -2300,6 +2300,57 @@ const ( StageInstancePrivacyLevelGuildOnly StageInstancePrivacyLevel = 2 ) +// PollLayoutType represents the layout of a poll. +type PollLayoutType int + +// Valid PollLayoutType values. +const ( + PollLayoutTypeDefault PollLayoutType = 1 +) + +// PollMedia contains common data used by question and answers. +type PollMedia struct { + Text string `json:"text,omitempty"` + Emoji *ComponentEmoji `json:"emoji,omitempty"` // TODO: rename the type +} + +// PollAnswer represents a single answer in a poll. +type PollAnswer struct { + // NOTE: should not be set on creation. + AnswerID int `json:"answer_id,omitempty"` + Media *PollMedia `json:"poll_media"` +} + +// PollAnswerCount stores counted poll votes for a single answer. +type PollAnswerCount struct { + ID int `json:"id"` + Count int `json:"count"` + MeVoted bool `json:"me_voted"` +} + +// PollResults contains voting results on a poll. +type PollResults struct { + Finalized bool `json:"is_finalized"` + AnswerCounts []*PollAnswerCount `json:"answer_count"` +} + +// Poll contains all poll related data. +type Poll struct { + Question PollMedia `json:"question"` + Answers []PollAnswer `json:"answers"` + AllowMultiselect bool `json:"allow_multiselect"` + LayoutType PollLayoutType `json:"layout_type,omitempty"` + + // NOTE: should be set only on creation, when fetching use Expiry. + Duration int `json:"duration,omitempty"` + + // NOTE: available only when fetching. + + Results *PollResults `json:"results,omitempty"` + // NOTE: as Discord documentation notes, this field might be null even when fetching. + Expiry *time.Time `json:"expiry,omitempty"` +} + // Constants for the different bit offsets of text channel permissions const ( // Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels