Skip to content

Commit

Permalink
Merge pull request #573 from Kraigie/jb3/member-struct-additions
Browse files Browse the repository at this point in the history
Add missing fields from member API object
  • Loading branch information
jb3 authored May 3, 2024
2 parents 0f8b1f2 + 11fb861 commit 8f91997
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 14 deletions.
4 changes: 4 additions & 0 deletions lib/nostrum/constants.ex
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ defmodule Nostrum.Constants do
def cdn_icon(id, icon, image_format), do: "/icons/#{id}/#{icon}.#{image_format}"
def cdn_splash(id, splash, image_format), do: "/splashes/#{id}/#{splash}.#{image_format}"

def cdn_guild_avatar(guild_id, user_id, avatar_hash, image_format) do
"/guilds/#{guild_id}/users/#{user_id}/avatars/#{avatar_hash}.#{image_format}"
end

def thread_with_message(channel_id, message_id),
do: "/channels/#{channel_id}/messages/#{message_id}/threads"

Expand Down
92 changes: 78 additions & 14 deletions lib/nostrum/struct/guild/member.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,23 @@ defmodule Nostrum.Struct.Guild.Member do
```
"""

alias Nostrum.Permission
alias Nostrum.Struct.{Channel, Guild, User}
alias Nostrum.Struct.Guild.Role
alias Nostrum.{Snowflake, Util}
alias Nostrum.{Constants, Permission, Snowflake, Util}
import Bitwise

defstruct [
:user_id,
:nick,
:roles,
:joined_at,
:avatar,
:communication_disabled_until,
:deaf,
:flags,
:joined_at,
:mute,
:communication_disabled_until,
:premium_since
:nick,
:pending,
:premium_since,
:roles,
:user_id
]

defimpl String.Chars do
Expand Down Expand Up @@ -93,15 +95,40 @@ defmodule Nostrum.Struct.Guild.Member do
"""
@type premium_since :: DateTime.t() | nil

@typedoc """
Avatar hash of the custom avatar set by the user in the guild.
If animated, this is prefixed with `a_`.
You can use `avatar_url/3` to fetch a full-formed URL of this asset.
"""
@type avatar :: String.t() | nil

@typedoc """
Current guild member gate status. `false` if user has yet to pass the membership screening
configuration for the guild, `true` if the member has passed.
"""
@type pending :: boolean | nil

@typedoc """
Guild member flags represented as a bitset.
Look at the `Nostrum.Struct.Guild.Member.Flags` module for guidance parsing this value.
"""
@type flags :: pos_integer() | nil

@type t :: %__MODULE__{
user_id: user_id,
nick: nick,
roles: roles,
joined_at: joined_at,
avatar: avatar,
communication_disabled_until: communication_disabled_until,
deaf: deaf,
flags: flags,
joined_at: joined_at,
mute: mute,
communication_disabled_until: communication_disabled_until,
premium_since: premium_since
nick: nick,
pending: pending,
premium_since: premium_since,
roles: roles,
user_id: user_id
}

@doc ~S"""
Expand All @@ -120,6 +147,43 @@ defmodule Nostrum.Struct.Guild.Member do
"<@#{user_id}>"
end

@doc ~S"""
Returns a guild-specific avatar URL for a `Nostrum.Struct.Guild.Member`.
Supported formats are `png` (default), `jpg`, `webp` and `gif`.
As mentioned in the avatar hash typedoc, if the avatar hash begins with `a_`, the
avatar is animated and can be returned as a gif.
## Examples
```elixir
iex> member = %Nostrum.Struct.Guild.Member{
...> user_id: 165023948638126080,
...> avatar: "4c8319db8ea745275a1399f8f8aa74ab"
...> }
iex> guild_id = 1226944827137069107
iex> Nostrum.Struct.Guild.Member.avatar_url(member, guild_id)
"https://cdn.discordapp.com/guilds/1226944827137069107/users/165023948638126080/avatars/4c8319db8ea745275a1399f8f8aa74ab.png"
```
"""
@spec avatar_url(t, Nostrum.Struct.Guild.id()) :: String.t() | nil
def avatar_url(member, guild_id, image_format \\ "png")

def avatar_url(%{avatar: nil}, _, _) do
nil
end

def avatar_url(%{user_id: user_id, avatar: avatar}, guild_id, image_format) do
Constants.cdn_url() <>
Constants.cdn_guild_avatar(
guild_id,
user_id,
avatar,
image_format
)
end

@doc """
Returns a member's guild permissions.
Expand Down
104 changes: 104 additions & 0 deletions lib/nostrum/struct/guild/member/flags.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
defmodule Nostrum.Struct.Guild.Member.Flags do
@moduledoc """
Struct representing the flags a guild member can have.
"""

import Bitwise

defstruct did_rejoin: false,
completed_onboarding: false,
bypasses_verification: false,
started_onboarding: false

@typedoc """
Member has left and rejoined the guild
"""
@type did_rejoin :: boolean

@typedoc """
Member has completed onboarding
"""
@type completed_onboarding :: boolean

@typedoc """
Member is exempt from guild verification requirements
"""
@type bypasses_verification :: boolean

@typedoc """
Member has started onboarding
"""
@type started_onboarding :: boolean

@type flags :: %__MODULE__{
did_rejoin: did_rejoin,
completed_onboarding: completed_onboarding,
bypasses_verification: bypasses_verification,
started_onboarding: started_onboarding
}

@type t :: flags

@flag_values [
did_rejoin: 1 <<< 0,
completed_onboarding: 1 <<< 1,
bypasses_verification: 1 <<< 2,
started_onboarding: 1 <<< 3
]

@doc """
Constructs a flag struct based on an integer from the Discord API, normally from `t:Nostrum.Struct.Guild.Member.flags/0`.
## Examples
```elixir
iex> Nostrum.Struct.Guild.Member.Flags.from_integer(9)
%Nostrum.Struct.Guild.Member.Flags{
did_rejoin: true,
completed_onboarding: false,
bypasses_verification: false,
started_onboarding: true
}
```
"""
@spec from_integer(integer()) :: t
def from_integer(flag_value) do
boolean_list =
Enum.map(@flag_values, fn {flag, value} ->
{flag, (flag_value &&& value) == value}
end)

struct(__MODULE__, boolean_list)
end

@doc """
Convert a flag struct to an integer value.
## Examples
```elixir
iex> my_flags = %Nostrum.Struct.Guild.Member.Flags{
...> did_rejoin: true,
...> completed_onboarding: false,
...> bypasses_verification: false,
...> started_onboarding: true
...> }
iex> Nostrum.Struct.Guild.Member.Flags.to_integer(my_flags)
9
```
"""
@spec to_integer(t) :: integer()
def to_integer(flag_struct) do
booleans =
flag_struct
|> Map.from_struct()
|> Map.to_list()

Enum.reduce(booleans, 0, fn {flag, enabled}, flag_value ->
case enabled do
true -> flag_value ||| @flag_values[flag]
false -> flag_value
end
end)
end
end
7 changes: 7 additions & 0 deletions test/nostrum/struct/guild/member/flags_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule Nostrum.Struct.Guild.Member.FlagsTest do
use ExUnit.Case, async: true

alias Nostrum.Struct.Guild.Member.Flags

doctest Flags
end

0 comments on commit 8f91997

Please # to comment.