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 support for polls #534

Merged
merged 35 commits into from
Apr 18, 2024
Merged
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
425e660
Bump Credo to 1.7.5 to stop deprecation warnings
jb3 Apr 13, 2024
125bd92
Add structures for Discord polls
jb3 Apr 13, 2024
64f2848
Add the new `poll` attribute to Message types
jb3 Apr 13, 2024
d7a3f44
Meet Credo checks
jb3 Apr 13, 2024
7a14f9d
Add missing typespec to method Poll.create_poll/2
jb3 Apr 13, 2024
33fd626
Add democracy manifest propaganda
jb3 Apr 14, 2024
d346bbb
Add new gateway struct for poll vote changes
jb3 Apr 14, 2024
7414520
Add new dispatch logic for poll vote changes
jb3 Apr 14, 2024
11ba7e5
Add new gateway intents for poll vote changes
jb3 Apr 14, 2024
2ee7662
Update intents test with new values
jb3 Apr 14, 2024
a4ed73c
Add API route for fetching voters for a poll
jb3 Apr 14, 2024
594eb47
Add API route for expiring polls early
jb3 Apr 14, 2024
22ee041
Add new typing for new gateway events to consumer
jb3 Apr 14, 2024
09791c7
Add documentation for helper methods on Poll
jb3 Apr 14, 2024
c3e267e
Add tests for Poll helper methods
jb3 Apr 14, 2024
a6b897f
Update Polls API module naming and documentation
jb3 Apr 15, 2024
2774595
Documentation improvements in PollVoteChange
jb3 Apr 15, 2024
c79c6b8
Simplify Map creation in PollVoteChange
jb3 Apr 15, 2024
40c15c2
Documentation improvements in Poll struct
jb3 Apr 15, 2024
4528747
Further documentation improvements in Poll struct
jb3 Apr 15, 2024
109b0d4
Documentation and simplifications in MediaObject
jb3 Apr 15, 2024
83d1589
Documentation improvements in Poll Results struct
jb3 Apr 15, 2024
6c022f3
Update Api.get_poll_answer_voters!/4 with new name
jb3 Apr 15, 2024
29f11e9
mix format to remove whitespace
jb3 Apr 15, 2024
17049c8
Update documentation about pagination
jb3 Apr 15, 2024
abfed4b
Actually fix the get_poll_answer_voters!/4 method
jb3 Apr 15, 2024
ac62d3d
Replace types of PollVoteChange with ID types
jb3 Apr 15, 2024
879d5cb
Update PollVoteChange to link to Poll.answers type
jb3 Apr 15, 2024
a447c75
Correct typing in Message struct
jb3 Apr 15, 2024
f0fe367
Better document results attribute of Poll
jb3 Apr 15, 2024
4de7b2f
Simplify Map creation for Results struct
jb3 Apr 15, 2024
747119b
Stop Credo complaints about PollVoteChange
jb3 Apr 15, 2024
6733306
Add new `send_polls` permission
jb3 Apr 17, 2024
dd57fa1
Clarify pagination return value
jb3 Apr 17, 2024
688d558
Add clarification on layout_type attribute
jb3 Apr 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
@@ -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`.
55 changes: 54 additions & 1 deletion lib/nostrum/api.ex
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ defmodule Nostrum.Api do
Interaction,
Invite,
Message,
Message.Poll,
ThreadMember,
User,
Webhook
@@ -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
@@ -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.
6 changes: 6 additions & 0 deletions lib/nostrum/constants.ex
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions lib/nostrum/consumer.ex
Original file line number Diff line number Diff line change
@@ -83,6 +83,7 @@ defmodule Nostrum.Consumer do
MessageReactionRemove,
MessageReactionRemoveAll,
MessageReactionRemoveEmoji,
PollVoteChange,
Ready,
SpeakingUpdate,
ThreadListSync,
@@ -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
@@ -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
4 changes: 3 additions & 1 deletion lib/nostrum/permission.ex
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ defmodule Nostrum.Permission do
| :view_guild_insights
| :use_application_commands
| :moderate_members
| :send_polls

@type text_permission ::
:add_reactions
@@ -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)
9 changes: 9 additions & 0 deletions lib/nostrum/shard/dispatch.ex
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ defmodule Nostrum.Shard.Dispatch do
MessageReactionRemove,
MessageReactionRemoveAll,
MessageReactionRemoveEmoji,
PollVoteChange,
Ready,
SpeakingUpdate,
ThreadListSync,
@@ -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}
4 changes: 3 additions & 1 deletion lib/nostrum/shard/intents.ex
Original file line number Diff line number Diff line change
@@ -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

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
@@ -14,6 +14,7 @@ defmodule Nostrum.Struct.Message do
Application,
Attachment,
Component,
Poll,
Reaction,
Reference,
Sticker
@@ -43,6 +44,7 @@ defmodule Nostrum.Struct.Message do
:message_reference,
:nonce,
:pinned,
:poll,
:reactions,
:referenced_message,
:sticker_items,
@@ -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()]
@@ -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,
@@ -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}))
jchristgit marked this conversation as resolved.
Show resolved Hide resolved
|> 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}}))
191 changes: 191 additions & 0 deletions lib/nostrum/struct/message/poll.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
defmodule Nostrum.Struct.Message.Poll do
@moduledoc """
Struct representing a poll in a Discord chat.
There are various helper methods on this structure to create new poll, see `create_poll/2` and `put_answer/2` & `put_answer/3` for code samples.
"""

alias Nostrum.Util

alias Nostrum.Snowflake
alias Nostrum.Struct.Message.Poll.{Answer, MediaObject, Results}

@derive Jason.Encoder
defstruct [
:question,
:answers,
:expiry,
:duration,
:allow_multiselect,
:layout_type,
:results
]

@typedoc """
Question for the poll
"""
@type question :: MediaObject.t()

@typedoc """
List of potential answers for the poll
"""
@type answers :: [Answer.t()]

@typedoc """
Expiry time of the poll
"""
@type expiry :: DateTime.t() | nil

@typedoc """
Duration of poll in hours
"""
@type duration :: integer | nil

@typedoc """
Whether the poll allows selection of multiple answers
"""
@type allow_multiselect :: boolean

@typedoc """
Layout type for the poll, currently only 1 (`DEFAULT`) is supported here.
If set to `nil`, the value will default to `1` at the Discord API.
"""
@type layout_type :: 1 | nil
jb3 marked this conversation as resolved.
Show resolved Hide resolved

@typedoc """
Result counts of a poll that has been voted on.
This field is only present for poll objects received over the gateway or Discord API.
As mentioned in the `Nostrum.Struct.Message.Poll.Results` documentation, if an answer has not been voted on it
will not be in this object.
"""
@type results :: Results.t() | nil

@type t :: %__MODULE__{
question: question,
answers: answers,
expiry: expiry,
duration: duration,
allow_multiselect: allow_multiselect,
layout_type: layout_type,
results: results
}

@doc false
def to_struct(map) do
new =
map
|> Map.new(fn {k, v} -> {Util.maybe_to_atom(k), v} end)
|> Map.update(:expiry, nil, &Util.maybe_to_datetime/1)
|> Map.update(:question, nil, &Util.cast(&1, {:struct, MediaObject}))
|> Map.update(:answers, nil, &Util.cast(&1, {:list, {:struct, Answer}}))
|> Map.update(:results, nil, &Util.cast(&1, {:struct, Results}))

struct(__MODULE__, new)
end

@doc ~S"""
Create a new poll struct.
Use `Nostrum.Api.create_message` to send it once you've populated it.
Accepts a `question_text` parameter which is the string to use as the poll title.
Keyword arguments:
- `duration`: duration (in hours) the poll should be open for
- `allow_multiselect`: whether users should be able to select multiple answers
You can also pass an `answers` key with answers, though `put_answer/2` and `put_answer/3` are advised.
## Examples
```elixir
poll = Poll.create_poll("Do you enjoy pineapple on pizza?", duration: 2, allow_multiselect: false)
|> Poll.put_answer("Yes!", default_emoji: "\u2705") # check mark emoji
|> Poll.put_answer("No!", default_emoji: "\u274C") # cross emoji
Api.create_message(channel_id, poll: poll)
```
"""
@spec create_poll(String.t(), duration: duration, allow_multiselect: allow_multiselect) :: t()
def create_poll(question_text, duration: duration, allow_multiselect: allow_multiselect) do
%__MODULE__{
question: %MediaObject{
text: question_text
},
answers: [],
duration: duration,
allow_multiselect: allow_multiselect,
layout_type: 1
}
end

@spec create_poll(String.t(),
duration: duration,
allow_multiselect: allow_multiselect,
answers: [Answer.t()]
) :: t()
def create_poll(question_text,
duration: duration,
allow_multiselect: allow_multiselect,
answers: answers
) do
poll = create_poll(question_text, duration: duration, allow_multiselect: allow_multiselect)

Map.put(poll, :answers, answers)
end

defp add_answer(poll, answer) do
Map.update(poll, :answers, [answer], fn answers -> answers ++ [answer] end)
end

@doc ~S"""
Add an answer to the provided poll.
See `create_poll/2` for a code sample of this function.
Takes a required "answer" text field, as well as either of the optional arguments:
- `custom_emoji`: An integer representing the snowflake of an emoji to display with the option
- `default_emoji`: A default platform emoji represented as a unicode character
"""
@spec put_answer(t(), String.t()) :: t()
def put_answer(poll, answer) do
new_answer = %Answer{
poll_media: %MediaObject{
text: answer
}
}

add_answer(poll, new_answer)
end

@spec put_answer(t(), String.t(), custom_emoji: Snowflake.t()) :: t()
def put_answer(poll, answer, custom_emoji: custom_emoji) do
new_answer = %Answer{
poll_media: %MediaObject{
text: answer,
emoji: %{
id: custom_emoji
}
}
}

add_answer(poll, new_answer)
end

@spec put_answer(t(), String.t(), default_emoji: String.t()) :: t()
def put_answer(poll, answer, default_emoji: default_emoji) do
new_answer = %Answer{
poll_media: %MediaObject{
text: answer,
emoji: %{
name: default_emoji
}
}
}

add_answer(poll, new_answer)
end
end
40 changes: 40 additions & 0 deletions lib/nostrum/struct/message/poll/answer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule Nostrum.Struct.Message.Poll.Answer do
@moduledoc """
A struct representing a poll answer.
"""

alias Nostrum.Util

alias Nostrum.Struct.Message.Poll.MediaObject

@derive Jason.Encoder
defstruct [
:answer_id,
:poll_media
]

@typedoc """
ID of the answer, this is only sent *from* the gateway, you do not need to send this to the gateway.
"""
@type answer_id :: integer | nil

@typedoc """
Object representing how the answer is displayed visually, with the text and optional emojis.
"""
@type poll_media :: MediaObject.t()

@type t :: %__MODULE__{
answer_id: answer_id,
poll_media: poll_media
}

@doc false
def to_struct(map) do
new =
map
|> Map.new(fn {k, v} -> {Util.maybe_to_atom(k), v} end)
|> Map.update(:poll_media, nil, &Util.cast(&1, {:struct, MediaObject}))

struct(__MODULE__, new)
end
end
38 changes: 38 additions & 0 deletions lib/nostrum/struct/message/poll/media_object.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
defmodule Nostrum.Struct.Message.Poll.MediaObject do
@moduledoc """
A struct representing a media item of a poll (e.g. a question or answer)
"""

alias Nostrum.Util

@derive Jason.Encoder
defstruct [
:text,
:emoji
]

@typedoc """
Text of the poll media object, either the question or answer text.
"""
@type text :: String.t() | nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be nil? In which cases is it nil? It would be nice to document it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, we don't know. This is the typing that has been given by the Discord API.

Wording from the upstream PR:

text should always be non-null for both questions and answers, but please do not depend on that in the future.

I guess we can come back and type it as nullable later in time if they add that feature, but this current spec meets the upstream types.


@typedoc """
A partial emoji (only supported for answers).
For a custom emoji, only the `id` field needs to be sent, for a default emoji, only the
`name` field needs to be sent (with the Unicode emoji).
"""
@type emoji :: %{id: integer | nil, name: String.t() | nil}

@type t :: %__MODULE__{
text: text,
emoji: emoji
}

@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
40 changes: 40 additions & 0 deletions lib/nostrum/struct/message/poll/results.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule Nostrum.Struct.Message.Poll.Results do
@moduledoc """
A struct representing the results of a poll.
"""

alias Nostrum.Util

defstruct [
:is_finalized,
:answer_counts
]

@typedoc """
A flag on whether the poll has finished counting.
If this is set to true, the counts are guaranteed to be accurate from Discord.
"""
@type is_finalized :: boolean

@typedoc """
A list of objects representing the counts for each of the potential answers in the poll.
The `id` property of each item corresponds to the `answer_id`
of the poll answers. If an option is not present in this list, then
there were no votes for that answer.
"""
@type answer_counts :: [%{id: integer, count: integer, me_voted: boolean}]

@type t :: %__MODULE__{
is_finalized: is_finalized,
answer_counts: answer_counts
}

@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
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -157,7 +157,7 @@ defmodule Nostrum.Mixfile do
{:mime, "~> 1.6 or ~> 2.0"},
{:castle, "~> 0.3.0", runtime: false},
{:ex_doc, "~> 0.28", only: :dev, runtime: false},
{:credo, "~> 1.4", only: [:dev, :test], runtime: false},
{:credo, "~> 1.7.5", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.1", only: [:dev], runtime: false},
{:benchee, "~> 1.1", only: :dev, runtime: false},
{:recon, "~> 2.3", only: :dev, optional: true}
8 changes: 4 additions & 4 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
%{
"benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"},
"benchee_json": {:hex, :benchee_json, "1.0.0", "cc661f4454d5995c08fe10dd1f2f72f229c8f0fb1c96f6b327a8c8fc96a91fe5", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "da05d813f9123505f870344d68fb7c86a4f0f9074df7d7b7e2bb011a63ec231c"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"castle": {:hex, :castle, "0.3.0", "47b1a550b2348a6d7e60e43ded1df19dca601ed21ef6f267c3dbb1b3a301fbf5", [:mix], [{:forecastle, "~> 0.1.0", [hex: :forecastle, repo: "hexpm", optional: false]}], "hexpm", "dbdc1c171520c4591101938a3d342dec70d36b7f5b102a5c138098581e35fcef"},
"certifi": {:hex, :certifi, "2.11.0", "5adfe37ceb8569d019f836944aeaf27f8ac391dacaf3707f570c155b7e03aaa8", [:rebar3], [], "hexpm", "9e37e0542ec3fabaa19a0734b3900dc095797fac48c40a2a9741d8ad5e3c9bb7"},
"chacha20": {:hex, :chacha20, "1.0.4", "0359d8f9a32269271044c1b471d5cf69660c362a7c61a98f73a05ef0b5d9eb9e", [:mix], [], "hexpm", "2027f5d321ae9903f1f0da7f51b0635ad6b8819bc7fe397837930a2011bc2349"},
"cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"},
"credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"},
"credo": {:hex, :credo, "1.7.5", "643213503b1c766ec0496d828c90c424471ea54da77c8a168c725686377b9545", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f799e9b5cd1891577d8c773d245668aa74a2fcd15eb277f51a0131690ebfb3fd"},
"curve25519": {:hex, :curve25519, "1.0.5", "f801179424e4012049fcfcfcda74ac04f65d0ffceeb80e7ef1d3352deb09f5bb", [:mix], [], "hexpm", "0fba3ad55bf1154d4d5fc3ae5fb91b912b77b13f0def6ccb3a5d58168ff4192d"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"},
@@ -15,10 +15,10 @@
"equivalex": {:hex, :equivalex, "1.0.3", "170d9a82ae066e0020dfe1cf7811381669565922eb3359f6c91d7e9a1124ff74", [:mix], [], "hexpm", "46fa311adb855117d36e461b9c0ad2598f72110ad17ad73d7533c78020e045fc"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"forecastle": {:hex, :forecastle, "0.1.1", "89dcfaccbfffe866cbd8a4c41ade55f62f00f1b5d0528bec787b1e6631004b98", [:mix], [], "hexpm", "f6f4d297224a22ac4387d305249aed7b8b02e85b4a03e83225af4536812c4079"},
"gun": {:hex, :gun, "2.0.1", "160a9a5394800fcba41bc7e6d421295cf9a7894c2252c0678244948e3336ad73", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "a10bc8d6096b9502205022334f719cc9a08d9adcfbfc0dbee9ef31b56274a20b"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"kcl": {:hex, :kcl, "1.4.2", "8b73a55a14899dc172fcb05a13a754ac171c8165c14f65043382d567922f44ab", [:mix], [{:curve25519, ">= 1.0.4", [hex: :curve25519, repo: "hexpm", optional: false]}, {:ed25519, "~> 1.3", [hex: :ed25519, repo: "hexpm", optional: false]}, {:poly1305, "~> 1.0", [hex: :poly1305, repo: "hexpm", optional: false]}, {:salsa20, "~> 1.0", [hex: :salsa20, repo: "hexpm", optional: false]}], "hexpm", "9f083dd3844d902df6834b258564a82b21a15eb9f6acdc98e8df0c10feeabf05"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
4 changes: 2 additions & 2 deletions test/nostrum/shard/intents_test.exs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ defmodule Nostrum.Shard.IntentsTest do
result = Intents.get_enabled_intents()

# Value of all intents
expected = 3_276_799
expected = 53_608_447

assert(^result = expected)
end
@@ -21,7 +21,7 @@ defmodule Nostrum.Shard.IntentsTest do
result = Intents.get_enabled_intents()

# Value of all non-privileged intents
expected = 3_276_799 - (2 + 256 + 32_768)
expected = 53_608_447 - (2 + 256 + 32_768)

assert(^result = expected)
end
40 changes: 40 additions & 0 deletions test/nostrum/struct/message/poll_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule Nostrum.Struct.Message.PollTest do
use ExUnit.Case, async: true

alias Nostrum.Struct.Message.Poll

doctest Poll

test "Poll.create_poll/2" do
assert Poll.create_poll("Craigs Cats!", duration: 1, allow_multiselect: true) ==
%Nostrum.Struct.Message.Poll{
question: %Nostrum.Struct.Message.Poll.MediaObject{
text: "Craigs Cats!",
emoji: nil
},
answers: [],
expiry: nil,
duration: 1,
allow_multiselect: true,
layout_type: 1,
results: nil
}
end

test "Poll.put_answer/2" do
poll =
Poll.create_poll("Craigs Cats!", duration: 1, allow_multiselect: true)
|> Poll.put_answer("Yes!")

assert Enum.at(poll.answers, 0).poll_media.text == "Yes!"
end

test "Poll.put_answer/3" do
poll =
Poll.create_poll("Craigs Cats!", duration: 1, allow_multiselect: true)
|> Poll.put_answer("Yes!", custom_emoji: 112_233_445_566_778_899)

assert Enum.at(poll.answers, 0).poll_media.text == "Yes!"
assert Enum.at(poll.answers, 0).poll_media.emoji.id == 112_233_445_566_778_899
end
end