Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add polls #346

Merged
merged 18 commits into from Apr 22, 2024
6 changes: 3 additions & 3 deletions discord/emoji.go
Expand Up @@ -66,9 +66,9 @@ type EmojiUpdate struct {
}

type PartialEmoji struct {
ID *snowflake.ID `json:"id"`
Name *string `json:"name"`
Animated bool `json:"animated"`
ID *snowflake.ID `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Animated bool `json:"animated,omitempty"`
}

// Reaction returns a string used for manipulating with reactions. May be empty if the Name is nil
Expand Down
1 change: 1 addition & 0 deletions discord/message.go
Expand Up @@ -114,6 +114,7 @@ type Message struct {
Position *int `json:"position,omitempty"`
RoleSubscriptionData *RoleSubscriptionData `json:"role_subscription_data,omitempty"`
Resolved *ResolvedData `json:"resolved,omitempty"`
Poll *Poll `json:"poll,omitempty"`
}

func (m *Message) UnmarshalJSON(data []byte) error {
Expand Down
1 change: 1 addition & 0 deletions discord/message_create.go
Expand Up @@ -18,6 +18,7 @@ type MessageCreate struct {
MessageReference *MessageReference `json:"message_reference,omitempty"`
Flags MessageFlags `json:"flags,omitempty"`
EnforceNonce bool `json:"enforce_nonce,omitempty"`
Poll *PollCreate `json:"poll,omitempty"`
}

func (MessageCreate) interactionCallbackData() {}
Expand Down
12 changes: 12 additions & 0 deletions discord/message_create_builder.go
Expand Up @@ -251,6 +251,18 @@ func (b *MessageCreateBuilder) SetSuppressEmbeds(suppressEmbeds bool) *MessageCr
return b
}

// SetPoll sets the Poll of the Message
func (b *MessageCreateBuilder) SetPoll(poll PollCreate) *MessageCreateBuilder {
sebm253 marked this conversation as resolved.
Show resolved Hide resolved
b.Poll = &poll
return b
}

// ClearPoll clears the Poll of the Message
func (b *MessageCreateBuilder) ClearPoll() *MessageCreateBuilder {
b.Poll = nil
return b
}

// Build builds the MessageCreateBuilder to a MessageCreate struct
func (b *MessageCreateBuilder) Build() MessageCreate {
return b.MessageCreate
Expand Down
7 changes: 6 additions & 1 deletion discord/permissions.go
Expand Up @@ -63,6 +63,9 @@ const (
PermissionCreateEvents
PermissionUseExternalSounds
PermissionSendVoiceMessages
_
_
PermissionSendPolls

PermissionsAllText = PermissionViewChannel |
PermissionSendMessages |
Expand All @@ -72,7 +75,8 @@ const (
PermissionAttachFiles |
PermissionReadMessageHistory |
PermissionMentionEveryone |
PermissionSendVoiceMessages
PermissionSendVoiceMessages |
PermissionSendPolls

PermissionsAllThread = PermissionManageThreads |
PermissionCreatePublicThreads |
Expand Down Expand Up @@ -170,6 +174,7 @@ var permissions = map[Permissions]string{
PermissionStream: "Video",
PermissionViewGuildInsights: "View Server Insights",
PermissionSendVoiceMessages: "Send Voice Messages",
PermissionSendPolls: "Create Polls",
}

func (p Permissions) String() string {
Expand Down
70 changes: 70 additions & 0 deletions discord/poll.go
@@ -0,0 +1,70 @@
package discord

import (
"time"

"github.com/disgoorg/json"
"github.com/disgoorg/snowflake/v2"
)

type Poll struct {
Question PollMedia `json:"question"`
Answers []PollAnswer `json:"answers"`
Expiry *time.Time `json:"expiry"`
AllowMultiselect bool `json:"allow_multiselect"`
LayoutType PollLayoutType `json:"layout_type"`
Results *PollResults `json:"results"`
}

type PollCreate struct {
Question PollMedia `json:"question"`
Answers []PollMedia `json:"-"`
Duration int `json:"duration"`
AllowMultiselect bool `json:"allow_multiselect"`
LayoutType PollLayoutType `json:"layout_type,omitempty"`
}

func (p PollCreate) MarshalJSON() ([]byte, error) {
type pollCreate PollCreate

answers := make([]PollAnswer, 0, len(p.Answers))
for _, answer := range p.Answers {
answers = append(answers, PollAnswer{
PollMedia: answer,
})
}
return json.Marshal(struct {
Answers []PollAnswer `json:"answers"`
pollCreate
}{
Answers: answers,
pollCreate: pollCreate(p),
})
}

type PollMedia struct {
Text *string `json:"text"`
Emoji *PartialEmoji `json:"emoji,omitempty"`
}

type PollAnswer struct {
AnswerID *int `json:"answer_id,omitempty"`
PollMedia PollMedia `json:"poll_media"`
}

type PollResults struct {
IsFinalized bool `json:"is_finalized"`
AnswerCounts []PollAnswerCount `json:"answer_counts"`
}

type PollAnswerCount struct {
ID snowflake.ID `json:"id"`
Count int `json:"count"`
MeVoted bool `json:"me_voted"`
}

type PollLayoutType int

const (
PollLayoutTypeDefault PollLayoutType = iota + 1
)
66 changes: 66 additions & 0 deletions discord/poll_create_builder.go
@@ -0,0 +1,66 @@
package discord

// PollCreateBuilder helps create PollCreate structs easier
type PollCreateBuilder struct {
PollCreate
}

// SetQuestion sets the question of the Poll
func (b *PollCreateBuilder) SetQuestion(text string) *PollCreateBuilder {
b.Question = PollMedia{
Text: &text,
}
return b
}

// SetPollAnswers sets the answers of the Poll
func (b *PollCreateBuilder) SetPollAnswers(answers ...PollMedia) *PollCreateBuilder {
b.Answers = answers
return b
}

// AddPollAnswer adds an answer to the Poll
func (b *PollCreateBuilder) AddPollAnswer(text string, emoji *PartialEmoji) *PollCreateBuilder {
b.Answers = append(b.Answers, PollMedia{
Text: &text,
Emoji: emoji,
})
return b
}

// RemoveAnswer removes an answer from the Poll
func (b *PollCreateBuilder) RemoveAnswer(i int) *PollCreateBuilder {
if len(b.Answers) > i {
b.Answers = append(b.Answers[:i], b.Answers[i+1:]...)
}
return b
}

// ClearAnswers removes all answers of the Poll
func (b *PollCreateBuilder) ClearAnswers() *PollCreateBuilder {
b.Answers = []PollMedia{}
return b
}

// SetDuration sets the duration of the Poll (in seconds)
sebm253 marked this conversation as resolved.
Show resolved Hide resolved
func (b *PollCreateBuilder) SetDuration(duration int) *PollCreateBuilder {
b.Duration = duration
return b
}

// SetAllowMultiselect sets whether users will be able to vote for more than one answer of the Poll
func (b *PollCreateBuilder) SetAllowMultiselect(multiselect bool) *PollCreateBuilder {
b.AllowMultiselect = multiselect
return b
}

// SetLayoutType sets the layout of the Poll
func (b *PollCreateBuilder) SetLayoutType(layout PollLayoutType) *PollCreateBuilder {
b.LayoutType = layout
return b
}

// Build builds the PollCreateBuilder to a PollCreate struct
func (b *PollCreateBuilder) Build() PollCreate {
return b.PollCreate
}
1 change: 1 addition & 0 deletions discord/webhook_message_create.go
Expand Up @@ -15,6 +15,7 @@ type WebhookMessageCreate struct {
Flags MessageFlags `json:"flags,omitempty"`
ThreadName string `json:"thread_name,omitempty"`
AppliedTags []snowflake.ID `json:"applied_tags,omitempty"`
Poll *PollCreate `json:"poll,omitempty"`
}

// ToBody returns the MessageCreate ready for body
Expand Down
12 changes: 12 additions & 0 deletions discord/webhook_message_create_builder.go
Expand Up @@ -210,6 +210,18 @@ func (b *WebhookMessageCreateBuilder) SetThreadName(threadName string) *WebhookM
return b
}

// SetPoll sets the Poll of the webhook Message
func (b *WebhookMessageCreateBuilder) SetPoll(poll PollCreate) *WebhookMessageCreateBuilder {
sebm253 marked this conversation as resolved.
Show resolved Hide resolved
b.Poll = &poll
return b
}

// ClearPoll clears the Poll of the webhook Message
func (b *WebhookMessageCreateBuilder) ClearPoll() *WebhookMessageCreateBuilder {
b.Poll = nil
return b
}

// Build builds the WebhookMessageCreateBuilder to a MessageCreate struct
func (b *WebhookMessageCreateBuilder) Build() WebhookMessageCreate {
b.WebhookMessageCreate.Components = b.Components
Expand Down
24 changes: 24 additions & 0 deletions events/dm_message_poll_events.go
@@ -0,0 +1,24 @@
package events

import (
"github.com/disgoorg/snowflake/v2"
)

// GenericDMMessagePollVote is called upon receiving DMMessagePollVoteAdd or DMMessagePollVoteRemove (requires gateway.IntentDirectMessagePolls)
type GenericDMMessagePollVote struct {
*GenericEvent
UserID snowflake.ID
ChannelID snowflake.ID
MessageID snowflake.ID
AnswerID int
}

// DMMessagePollVoteAdd indicates that a discord.User voted on a discord.Poll in a DM (requires gateway.IntentDirectMessagePolls)
type DMMessagePollVoteAdd struct {
*GenericDMMessagePollVote
}

// DMMessagePollVoteRemove indicates that a discord.User removed their vote on a discord.Poll in a DM (requires gateway.IntentDirectMessagePolls)
type DMMessagePollVoteRemove struct {
*GenericDMMessagePollVote
}
36 changes: 36 additions & 0 deletions events/guild_message_poll_events.go
@@ -0,0 +1,36 @@
package events

import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/snowflake/v2"
)

// GenericGuildMessagePollVote is called upon receiving GuildMessagePollVoteAdd or GuildMessagePollVoteRemove (requires gateway.IntentGuildMessagePolls)
type GenericGuildMessagePollVote struct {
topi314 marked this conversation as resolved.
Show resolved Hide resolved
*GenericEvent
UserID snowflake.ID
ChannelID snowflake.ID
MessageID snowflake.ID
GuildID snowflake.ID
AnswerID int
}

// Guild returns the discord.Guild where the GenericGuildMessagePollVote happened
func (e *GenericGuildMessagePollVote) Guild() (discord.Guild, bool) {
return e.Client().Caches().Guild(e.GuildID)
}

// Channel returns the discord.GuildMessageChannel where the GenericGuildMessagePollVote happened
func (e *GenericGuildMessagePollVote) Channel() (discord.GuildMessageChannel, bool) {
return e.Client().Caches().GuildMessageChannel(e.ChannelID)
}

// GuildMessagePollVoteAdd indicates that a discord.User voted on a discord.Poll in a discord.Guild (requires gateway.IntentGuildMessagePolls)
type GuildMessagePollVoteAdd struct {
*GenericGuildMessagePollVote
}

// GuildMessagePollVoteRemove indicates that a discord.User removed their vote on a discord.Poll in a discord.Guild (requires gateway.IntentGuildMessagePolls)
type GuildMessagePollVoteRemove struct {
*GenericGuildMessagePollVote
}
38 changes: 38 additions & 0 deletions events/listener_adapter.go
Expand Up @@ -147,6 +147,18 @@ type ListenerAdapter struct {
OnMessageUpdate func(event *MessageUpdate)
OnMessageDelete func(event *MessageDelete)

// Message Poll Events
sebm253 marked this conversation as resolved.
Show resolved Hide resolved
OnMessagePollVoteAdd func(event *MessagePollVoteAdd)
OnMessagePollVoteRemove func(event *MessagePollVoteRemove)

// DM Message Poll Events
OnDMMessagePollVoteAdd func(event *DMMessagePollVoteAdd)
OnDMMessagePollVoteRemove func(event *DMMessagePollVoteRemove)

// Guild Message Poll Events
OnGuildMessagePollVoteAdd func(event *GuildMessagePollVoteAdd)
OnGuildMessagePollVoteRemove func(event *GuildMessagePollVoteRemove)

// Message Reaction Events
OnMessageReactionAdd func(event *MessageReactionAdd)
OnMessageReactionRemove func(event *MessageReactionRemove)
Expand Down Expand Up @@ -574,6 +586,32 @@ func (l *ListenerAdapter) OnEvent(event bot.Event) {
listener(e)
}

// Message Poll Events
case *MessagePollVoteAdd:
if listener := l.OnMessagePollVoteAdd; listener != nil {
listener(e)
}
case *MessagePollVoteRemove:
if listener := l.OnMessagePollVoteRemove; listener != nil {
listener(e)
}
case *DMMessagePollVoteAdd:
if listener := l.OnDMMessagePollVoteAdd; listener != nil {
listener(e)
}
case *DMMessagePollVoteRemove:
if listener := l.OnDMMessagePollVoteRemove; listener != nil {
listener(e)
}
case *GuildMessagePollVoteAdd:
if listener := l.OnGuildMessagePollVoteAdd; listener != nil {
listener(e)
}
case *GuildMessagePollVoteRemove:
if listener := l.OnGuildMessagePollVoteRemove; listener != nil {
listener(e)
}

// Message Reaction Events
case *MessageReactionAdd:
if listener := l.OnMessageReactionAdd; listener != nil {
Expand Down
34 changes: 34 additions & 0 deletions events/message_poll_events.go
@@ -0,0 +1,34 @@
package events

import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/snowflake/v2"
)

// GenericMessagePollVote is a generic poll vote event (requires gateway.IntentGuildMessagePolls and/or gateway.IntentDirectMessagePolls)
type GenericMessagePollVote struct {
topi314 marked this conversation as resolved.
Show resolved Hide resolved
*GenericEvent
UserID snowflake.ID
ChannelID snowflake.ID
MessageID snowflake.ID
GuildID *snowflake.ID
AnswerID int
}

// Guild returns the discord.Guild where the GenericMessagePollVote happened or empty if it happened in DMs
func (e *GenericMessagePollVote) Guild() (discord.Guild, bool) {
if e.GuildID == nil {
return discord.Guild{}, false
}
return e.Client().Caches().Guild(*e.GuildID)
}

// MessagePollVoteAdd indicates that a discord.User voted on a discord.Poll (requires gateway.IntentGuildMessagePolls and/or gateway.IntentDirectMessagePolls)
type MessagePollVoteAdd struct {
*GenericMessagePollVote
}

// MessagePollVoteRemove indicates that a discord.User removed their vote on a discord.Poll (requires gateway.IntentGuildMessagePolls and/or gateway.IntentDirectMessagePolls)
type MessagePollVoteRemove struct {
*GenericMessagePollVote
}
2 changes: 2 additions & 0 deletions gateway/gateway_event_type.go
Expand Up @@ -59,6 +59,8 @@ const (
EventTypeMessageUpdate EventType = "MESSAGE_UPDATE"
EventTypeMessageDelete EventType = "MESSAGE_DELETE"
EventTypeMessageDeleteBulk EventType = "MESSAGE_DELETE_BULK"
EventTypeMessagePollVoteAdd EventType = "MESSAGE_POLL_VOTE_ADD"
EventTypeMessagePollVoteRemove EventType = "MESSAGE_POLL_VOTE_REMOVE"
EventTypeMessageReactionAdd EventType = "MESSAGE_REACTION_ADD"
EventTypeMessageReactionRemove EventType = "MESSAGE_REACTION_REMOVE"
EventTypeMessageReactionRemoveAll EventType = "MESSAGE_REACTION_REMOVE_ALL"
Expand Down