feat: implement polls (#1525)

* feat: add poll events

* feat: add poll structs

* feat(MessageSend): add poll field

* feat(InteractionResponseData): add poll field

* feat: add endpoints
This commit is contained in:
Fedor Lapshin 2024-06-21 10:37:32 +03:00 committed by GitHub
parent a31fd8617e
commit d26ad10ba6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 175 additions and 0 deletions

View file

@ -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"
}

View file

@ -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))

View file

@ -407,3 +407,21 @@ type GuildAuditLogEntryCreate struct {
*AuditLogEntry
GuildID string `json:"guild_id"`
}
// 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"`
}

View file

@ -575,6 +575,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"`

View file

@ -242,6 +242,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:"-"`

View file

@ -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
}

View file

@ -2334,6 +2334,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