api-surface #1
5 changed files with 318 additions and 0 deletions
feat(model): Attachment, Embed family, Reaction, AllowedMentions
commit
e065b4d6c7
11
lib/dexcord/model/allowed_mentions.ex
Normal file
11
lib/dexcord/model/allowed_mentions.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
defmodule Dexcord.AllowedMentions do
|
||||
@moduledoc "Send-side allowed-mentions control. https://docs.discord.com/developers/resources/message"
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :parse, {:list, :string}, default: []
|
||||
field :roles, {:list, :snowflake}
|
||||
field :users, {:list, :snowflake}
|
||||
field :replied_user, :boolean, default: false
|
||||
end
|
||||
end
|
||||
26
lib/dexcord/model/attachment.ex
Normal file
26
lib/dexcord/model/attachment.ex
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
defmodule Dexcord.Attachment do
|
||||
@moduledoc "A message attachment. https://docs.discord.com/developers/resources/message"
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :id, :snowflake
|
||||
field :filename, :string
|
||||
field :title, :string
|
||||
field :description, :string
|
||||
field :content_type, :string
|
||||
field :size, :integer
|
||||
field :url, :string
|
||||
field :proxy_url, :string
|
||||
field :height, :integer
|
||||
field :width, :integer
|
||||
field :placeholder, :string
|
||||
field :placeholder_version, :integer
|
||||
field :ephemeral, :boolean
|
||||
field :duration_secs, :number
|
||||
field :waveform, :string
|
||||
field :flags, {:flags, Dexcord.AttachmentFlags}
|
||||
field :clip_participants, {:list, {:struct, Dexcord.User}}
|
||||
field :clip_created_at, :datetime
|
||||
field :application, :raw
|
||||
end
|
||||
end
|
||||
116
lib/dexcord/model/embed.ex
Normal file
116
lib/dexcord/model/embed.ex
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
defmodule Dexcord.Embed do
|
||||
@moduledoc "A message embed. https://docs.discord.com/developers/resources/message"
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :title, :string
|
||||
field :type, :string
|
||||
field :description, :string
|
||||
field :url, :string
|
||||
field :timestamp, :datetime
|
||||
field :color, :integer
|
||||
field :footer, {:struct, Dexcord.EmbedFooter}
|
||||
field :image, {:struct, Dexcord.EmbedImage}
|
||||
field :thumbnail, {:struct, Dexcord.EmbedThumbnail}
|
||||
field :video, {:struct, Dexcord.EmbedVideo}
|
||||
field :provider, {:struct, Dexcord.EmbedProvider}
|
||||
field :author, {:struct, Dexcord.EmbedAuthor}
|
||||
field :fields, {:list, {:struct, Dexcord.EmbedField}}, default: []
|
||||
field :flags, :integer
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.EmbedFooter do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :text, :string
|
||||
field :icon_url, :string
|
||||
field :proxy_icon_url, :string
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.EmbedImage do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :url, :string
|
||||
field :proxy_url, :string
|
||||
field :height, :integer
|
||||
field :width, :integer
|
||||
field :content_type, :string
|
||||
field :placeholder, :string
|
||||
field :placeholder_version, :integer
|
||||
field :description, :string
|
||||
field :flags, :integer
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.EmbedThumbnail do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :url, :string
|
||||
field :proxy_url, :string
|
||||
field :height, :integer
|
||||
field :width, :integer
|
||||
field :content_type, :string
|
||||
field :placeholder, :string
|
||||
field :placeholder_version, :integer
|
||||
field :description, :string
|
||||
field :flags, :integer
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.EmbedVideo do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :url, :string
|
||||
field :proxy_url, :string
|
||||
field :height, :integer
|
||||
field :width, :integer
|
||||
field :content_type, :string
|
||||
field :placeholder, :string
|
||||
field :placeholder_version, :integer
|
||||
field :description, :string
|
||||
field :flags, :integer
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.EmbedProvider do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :name, :string
|
||||
field :url, :string
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.EmbedAuthor do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :name, :string
|
||||
field :url, :string
|
||||
field :icon_url, :string
|
||||
field :proxy_icon_url, :string
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.EmbedField do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :name, :string
|
||||
field :value, :string
|
||||
field :inline, :boolean, default: false
|
||||
end
|
||||
end
|
||||
23
lib/dexcord/model/reaction.ex
Normal file
23
lib/dexcord/model/reaction.ex
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
defmodule Dexcord.Reaction do
|
||||
@moduledoc "A message reaction. https://docs.discord.com/developers/resources/message"
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :count, :integer
|
||||
field :count_details, {:struct, Dexcord.ReactionCountDetails}
|
||||
field :me, :boolean
|
||||
field :me_burst, :boolean
|
||||
field :emoji, {:struct, Dexcord.PartialEmoji}
|
||||
field :burst_colors, {:list, :string}, default: []
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.ReactionCountDetails do
|
||||
@moduledoc false
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :burst, :integer
|
||||
field :normal, :integer
|
||||
end
|
||||
end
|
||||
142
test/dexcord/model_message_parts_test.exs
Normal file
142
test/dexcord/model_message_parts_test.exs
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
defmodule Dexcord.ModelMessagePartsTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
describe "Embed.from_map/1" do
|
||||
test "a rich embed decodes fully typed with fields order preserved" do
|
||||
map = %{
|
||||
"title" => "Hello",
|
||||
"type" => "rich",
|
||||
"description" => "world",
|
||||
"url" => "https://example.com",
|
||||
"timestamp" => "2021-01-01T00:00:00.000000+00:00",
|
||||
"color" => 16_711_680,
|
||||
"footer" => %{
|
||||
"text" => "the footer",
|
||||
"icon_url" => "https://example.com/i.png",
|
||||
"proxy_icon_url" => "https://proxy/i.png"
|
||||
},
|
||||
"author" => %{
|
||||
"name" => "an author",
|
||||
"url" => "https://example.com/a",
|
||||
"icon_url" => "https://example.com/ai.png"
|
||||
},
|
||||
"image" => %{"url" => "https://example.com/img.png", "height" => 100, "width" => 200},
|
||||
"fields" => [
|
||||
%{"name" => "first", "value" => "1", "inline" => true},
|
||||
%{"name" => "second", "value" => "2", "inline" => false}
|
||||
]
|
||||
}
|
||||
|
||||
embed = Dexcord.Embed.from_map(map)
|
||||
|
||||
assert embed.title == "Hello"
|
||||
assert embed.type == "rich"
|
||||
assert embed.color == 16_711_680
|
||||
assert %DateTime{} = embed.timestamp
|
||||
|
||||
assert %Dexcord.EmbedFooter{} = embed.footer
|
||||
assert embed.footer.text == "the footer"
|
||||
|
||||
assert %Dexcord.EmbedAuthor{} = embed.author
|
||||
assert embed.author.name == "an author"
|
||||
|
||||
assert %Dexcord.EmbedImage{} = embed.image
|
||||
assert embed.image.height == 100
|
||||
assert embed.image.width == 200
|
||||
|
||||
assert [%Dexcord.EmbedField{} = f1, %Dexcord.EmbedField{} = f2] = embed.fields
|
||||
assert f1.name == "first"
|
||||
assert f1.inline == true
|
||||
assert f2.name == "second"
|
||||
assert f2.inline == false
|
||||
end
|
||||
|
||||
test "fields defaults to empty list when absent" do
|
||||
embed = Dexcord.Embed.from_map(%{"title" => "t"})
|
||||
assert embed.fields == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "Attachment.from_map/1" do
|
||||
test "duration_secs decodes to a float" do
|
||||
map = %{
|
||||
"id" => "1",
|
||||
"filename" => "voice-message.ogg",
|
||||
"size" => 12_345,
|
||||
"url" => "https://cdn/x.ogg",
|
||||
"proxy_url" => "https://proxy/x.ogg",
|
||||
"duration_secs" => 2.5
|
||||
}
|
||||
|
||||
att = Dexcord.Attachment.from_map(map)
|
||||
|
||||
assert att.id == 1
|
||||
assert att.filename == "voice-message.ogg"
|
||||
assert att.duration_secs == 2.5
|
||||
assert is_float(att.duration_secs)
|
||||
end
|
||||
|
||||
test "voice-message attachment keeps the raw flags int and waveform" do
|
||||
map = %{
|
||||
"id" => "1",
|
||||
"filename" => "voice-message.ogg",
|
||||
"size" => 100,
|
||||
"url" => "https://cdn/x.ogg",
|
||||
"proxy_url" => "https://proxy/x.ogg",
|
||||
"waveform" => "FzYACAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"flags" => 8194
|
||||
}
|
||||
|
||||
att = Dexcord.Attachment.from_map(map)
|
||||
|
||||
assert att.waveform == "FzYACAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
assert att.flags == 8194
|
||||
end
|
||||
end
|
||||
|
||||
describe "Reaction.from_map/1" do
|
||||
test "reaction with unicode partial emoji and count_details decodes" do
|
||||
map = %{
|
||||
"count" => 3,
|
||||
"count_details" => %{"burst" => 1, "normal" => 2},
|
||||
"me" => false,
|
||||
"me_burst" => false,
|
||||
"emoji" => %{"id" => nil, "name" => "🔥"},
|
||||
"burst_colors" => ["#ff0000"]
|
||||
}
|
||||
|
||||
reaction = Dexcord.Reaction.from_map(map)
|
||||
|
||||
assert reaction.count == 3
|
||||
assert %Dexcord.ReactionCountDetails{} = reaction.count_details
|
||||
assert reaction.count_details.burst == 1
|
||||
assert reaction.count_details.normal == 2
|
||||
assert %Dexcord.PartialEmoji{} = reaction.emoji
|
||||
assert reaction.emoji.id == nil
|
||||
assert reaction.emoji.name == "🔥"
|
||||
assert reaction.burst_colors == ["#ff0000"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "AllowedMentions.to_map/1" do
|
||||
test "default struct encodes declared defaults and omits nils" do
|
||||
out = %Dexcord.AllowedMentions{} |> Dexcord.AllowedMentions.to_map()
|
||||
assert out == %{"parse" => [], "replied_user" => false}
|
||||
end
|
||||
|
||||
test "decodes a populated allowed_mentions map" do
|
||||
map = %{
|
||||
"parse" => ["users"],
|
||||
"roles" => ["1"],
|
||||
"users" => ["2"],
|
||||
"replied_user" => true
|
||||
}
|
||||
|
||||
am = Dexcord.AllowedMentions.from_map(map)
|
||||
assert am.parse == ["users"]
|
||||
assert am.roles == [1]
|
||||
assert am.users == [2]
|
||||
assert am.replied_user == true
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue