Skip to content

Commit

Permalink
Merge pull request #534 from Kraigie/jb3/polls
Browse files Browse the repository at this point in the history
Add support for polls
  • Loading branch information
jb3 authored Apr 18, 2024
2 parents 8bcece0 + 688d558 commit a82e9f4
Show file tree
Hide file tree
Showing 18 changed files with 519 additions and 14 deletions.
Binary file added assets/propaganda/democracy_manifest.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 19 additions & 3 deletions guides/intro/gateway_intents.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,31 @@ direct_message_reactions:
direct_message_typing:
- TYPING_START
GUILD_SCHEDULED_EVENTS:
message_content*:
- MESSAGE_CONTENT
guild_scheduled_events:
- GUILD_SCHEDULED_EVENT_CREATE
- GUILD_SCHEDULED_EVENT_UPDATE
- GUILD_SCHEDULED_EVENT_DELETE
- GUILD_SCHEDULED_EVENT_USER_ADD
- GUILD_SCHEDULED_EVENT_USER_REMOVE
message_content*:
- MESSAGE_CONTENT
auto_moderation_configuration:
- AUTO_MODERATION_RULE_CREATE
- AUTO_MODERATION_RULE_DELETE
- AUTO_MODERATION_RULE_UPDATE
auto_moderation_execution:
- AUTO_MODERATION_RULE_EXECUTION
guild_message_polls:
- MESSAGE_POLL_VOTE_ADD
- MESSAGE_POLL_VOTE_REMOVE
direct_message_polls:
- MESSAGE_POLL_VOTE_ADD
- MESSAGE_POLL_VOTE_REMOVE
```

Besides an explicit list of atoms, acceptable configuration values are `:all` and `:nonprivileged`.
Expand Down
55 changes: 54 additions & 1 deletion lib/nostrum/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ defmodule Nostrum.Api do
Interaction,
Invite,
Message,
Message.Poll,
ThreadMember,
User,
Webhook
Expand Down Expand Up @@ -219,8 +220,9 @@ defmodule Nostrum.Api do
* `:embeds` (`t:Nostrum.Struct.Embed.t/0`) - a list of embedded rich content
* `:allowed_mentions` (`t:allowed_mentions/0`) - see the allowed mentions type documentation
* `:message_reference` (`map`) - See "Message references" below
* `:poll` (`t:Nostrum.Struct.Message.Poll.t/0`) - A poll object to send with the message
At least one of the following is required: `:content`, `:file`, `:embeds`.
At least one of the following is required: `:content`, `:file`, `:embeds`, `:poll`.
### Message reference
Expand Down Expand Up @@ -617,6 +619,57 @@ defmodule Nostrum.Api do
|> bangify
end

@doc ~S"""
Get voters for the provided answer on the poll attached to the provided message.
If successful, returns `{:ok, users}`. Otherwise, returns `t:Nostrum.Api.error/0`.
The optional `params` are `after`, the user ID to query after, absent by default,
and `limit`, the max number of users to return, 1-100, 25 by default. Results are
sorted by Discord user snowflake (ID) in ascending order.
"""
@spec get_poll_answer_voters(Channel.id(), Message.id(), Poll.Answer.answer_id()) ::
error | {:ok, [User.t()]}
def get_poll_answer_voters(channel_id, message_id, answer_id, params \\ []) do
result =
request(:get, Constants.poll_answer_voters(channel_id, message_id, answer_id), "", params)
|> handle_request_with_decode()

case result do
{:ok, %{users: users}} -> {:ok, Util.cast(users, {:list, {:struct, User}})}
_ -> result
end
end

@doc ~S"""
Same as `get_poll_answer_voters/4`, but raises `Nostrum.Error.ApiError` in case of failure.
"""
@spec get_poll_answer_voters!(Channel.id(), Message.id(), Poll.Answer.answer_id()) :: [User.t()]
def get_poll_answer_voters!(channel_id, message_id, answer_id, params \\ []) do
get_poll_answer_voters(channel_id, message_id, answer_id, params)
|> bangify
end

@doc ~S"""
Expire (close voting on) a poll before the scheduled end time.
Returns the original message containing the poll.
"""
@spec expire_poll(Channel.id(), Message.id()) :: error | {:ok, Message.t()}
def expire_poll(channel_id, message_id) do
request(:post, Constants.poll_expire(channel_id, message_id))
|> handle_request_with_decode({:struct, Message})
end

@doc ~S"""
Same as `expire_poll/2`, but raises `Nostrum.Error.ApiError` in case of failure.
"""
@spec expire_poll!(Channel.id(), Message.id()) :: Message.t()
def expire_poll!(channel_id, message_id) do
expire_poll(channel_id, message_id)
|> bangify
end

@doc ~S"""
Gets a channel.
Expand Down
6 changes: 6 additions & 0 deletions lib/nostrum/constants.ex
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@ defmodule Nostrum.Constants do
def guild_auto_moderation_rule(guild_id, rule_id),
do: "/guilds/#{guild_id}/auto-moderation/rules/#{rule_id}"

def poll_answer_voters(channel_id, message_id, answer_id),
do: "/channels/#{channel_id}/polls/#{message_id}/answers/#{answer_id}"

def poll_expire(channel_id, message_id),
do: "/channels/#{channel_id}/polls/#{message_id}/expire"

def discord_epoch, do: 1_420_070_400_000

def opcodes do
Expand Down
14 changes: 14 additions & 0 deletions lib/nostrum/consumer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ defmodule Nostrum.Consumer do
MessageReactionRemove,
MessageReactionRemoveAll,
MessageReactionRemoveEmoji,
PollVoteChange,
Ready,
SpeakingUpdate,
ThreadListSync,
Expand Down Expand Up @@ -325,6 +326,17 @@ defmodule Nostrum.Consumer do
Dispatched when member(s) are added or removed from a thread
"""
@type thread_members_update :: {:THREAD_MEMBERS_UPDATE, ThreadMembersUpdate.t(), WSState.t()}

@typedoc """
Dispatched when a user adds a vote to a poll.
"""
@type message_poll_vote_add :: {:MESSAGE_POLL_VOTE_ADD, PollVoteChange.t(), WSState.t()}

@typedoc """
Dispatched when a user removes a vote from a poll.
"""
@type message_poll_vote_remove :: {:MESSAGE_POLL_VOTE_REMVE, PollVoteChange.t(), WSState.t()}

@type event ::
auto_moderation_rule_create
| auto_moderation_rule_delete
Expand Down Expand Up @@ -364,6 +376,8 @@ defmodule Nostrum.Consumer do
| message_reaction_remove
| message_reaction_remove_all
| message_ack
| message_poll_vote_add
| message_poll_vote_remove
| presence_update
| ready
| resumed
Expand Down
4 changes: 3 additions & 1 deletion lib/nostrum/permission.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ defmodule Nostrum.Permission do
| :view_guild_insights
| :use_application_commands
| :moderate_members
| :send_polls

@type text_permission ::
:add_reactions
Expand Down Expand Up @@ -124,7 +125,8 @@ defmodule Nostrum.Permission do
use_external_stickers: 1 <<< 37,
send_messages_in_threads: 1 <<< 38,
use_embedded_activities: 1 <<< 39,
moderate_members: 1 <<< 40
moderate_members: 1 <<< 40,
send_polls: 1 <<< 49
}

@bit_to_permission_map Map.new(@permission_to_bit_map, fn {k, v} -> {v, k} end)
Expand Down
9 changes: 9 additions & 0 deletions lib/nostrum/shard/dispatch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ defmodule Nostrum.Shard.Dispatch do
MessageReactionRemove,
MessageReactionRemoveAll,
MessageReactionRemoveEmoji,
PollVoteChange,
Ready,
SpeakingUpdate,
ThreadListSync,
Expand Down Expand Up @@ -404,6 +405,14 @@ defmodule Nostrum.Shard.Dispatch do
{event, Interaction.to_struct(p), state}
end

def handle_event(:MESSAGE_POLL_VOTE_ADD = event, p, state) do
{event, PollVoteChange.to_struct(Map.merge(p, %{type: :add})), state}
end

def handle_event(:MESSAGE_POLL_VOTE_REMOVE = event, p, state) do
{event, PollVoteChange.to_struct(Map.merge(p, %{type: :remove})), state}
end

def handle_event(event, p, state) do
Logger.warning("UNHANDLED GATEWAY DISPATCH EVENT TYPE: #{event}, #{inspect(p)}")
{event, p, state}
Expand Down
4 changes: 3 additions & 1 deletion lib/nostrum/shard/intents.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ defmodule Nostrum.Shard.Intents do
message_content: 1 <<< 15,
guild_scheduled_events: 1 <<< 16,
auto_moderation_configuration: 1 <<< 20,
auto_moderation_execution: 1 <<< 21
auto_moderation_execution: 1 <<< 21,
guild_message_polls: 1 <<< 24,
direct_message_polls: 1 <<< 25
]
end

Expand Down
47 changes: 47 additions & 0 deletions lib/nostrum/struct/event/poll_vote_change.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule Nostrum.Struct.Event.PollVoteChange do
@moduledoc """
Represents an addition or removal of a vote from a Discord poll.
For polls where multiple answers were selected, one of these events will be fired for each vote.
"""
alias Nostrum.Util

alias Nostrum.Struct.{Channel, Guild, Message, User}

defstruct [:user_id, :channel_id, :message_id, :guild_id, :answer_id, :type]

@typedoc "ID of the user that has voted"
@type user_id :: User.id()

@typedoc "ID of the channel the vote took place in"
@type channel_id :: Channel.id()

@typedoc "ID of the message the poll was attached to"
@type message_id :: Message.id()

@typedoc "ID of the guild the poll is in (unless it is a private channel)"
@type guild_id :: Guild.id()

@typedoc "ID corresponding to the answer_id in the `t:Nostrum.Struct.Message.Poll.answers/0` list"
@type answer_id :: integer

@typedoc "Whether the vote was an addition or removal for a vote of the option"
@type type :: :add | :remove

@typedoc "Event representing a addition or removal of a vote from a poll"
@type t :: %__MODULE__{
user_id: user_id,
channel_id: channel_id,
message_id: message_id,
guild_id: guild_id,
answer_id: answer_id,
type: type
}

@doc false
def to_struct(map) do
new = Map.new(map, fn {k, v} -> {Util.maybe_to_atom(k), v} end)

struct(__MODULE__, new)
end
end
9 changes: 8 additions & 1 deletion lib/nostrum/struct/message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule Nostrum.Struct.Message do
Application,
Attachment,
Component,
Poll,
Reaction,
Reference,
Sticker
Expand Down Expand Up @@ -43,6 +44,7 @@ defmodule Nostrum.Struct.Message do
:message_reference,
:nonce,
:pinned,
:poll,
:reactions,
:referenced_message,
:sticker_items,
Expand Down Expand Up @@ -111,7 +113,10 @@ defmodule Nostrum.Struct.Message do
@typedoc """
Message interaction object
"""
@type interaction :: Interaction.t()
@type interaction :: Interaction.t() | nil

@typedoc "The poll object attached to the message"
@type poll :: Poll.t() | nil

@typedoc "List of embedded content in the message"
@type embeds :: [Embed.t()]
Expand Down Expand Up @@ -217,6 +222,7 @@ defmodule Nostrum.Struct.Message do
message_reference: message_reference,
nonce: nonce,
pinned: pinned,
poll: poll,
reactions: reactions,
referenced_message: referenced_message,
sticker_items: sticker_items,
Expand Down Expand Up @@ -250,6 +256,7 @@ defmodule Nostrum.Struct.Message do
|> Map.update(:mentions, nil, &Util.cast(&1, {:list, {:struct, User}}))
|> Map.update(:message_reference, nil, &Util.cast(&1, {:struct, Reference}))
|> Map.update(:nonce, nil, &Util.cast(&1, Snowflake))
|> Map.update(:poll, nil, &Util.cast(&1, {:struct, Poll}))
|> Map.update(:reactions, nil, &Util.cast(&1, {:list, {:struct, Reaction}}))
|> Map.update(:referenced_message, nil, &Util.cast(&1, {:struct, __MODULE__}))
|> Map.update(:sticker_items, nil, &Util.cast(&1, {:list, {:struct, Sticker}}))
Expand Down
Loading

0 comments on commit a82e9f4

Please sign in to comment.