mirror of
https://github.com/TheRealOwenRees/plantid-discord-bot.git
synced 2026-07-23 04:26:57 +00:00
image saving with basic error handling
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :plantid_discord_bot,
|
config :plantid_discord_bot,
|
||||||
image_folder: "priv/static"
|
image_path: "priv/static"
|
||||||
|
|
||||||
import_config "#{config_env()}.exs"
|
import_config "#{config_env()}.exs"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
defmodule PlantIdDiscordBot.Cog.PlantNet do
|
defmodule PlantIdDiscordBot.Cog.PlantNet do
|
||||||
use Nostrum.Consumer
|
use Nostrum.Consumer
|
||||||
# alias PlantIdDiscordBot.Cog.PlantNet
|
|
||||||
alias PlantIdDiscordBot.RateLimiter
|
alias PlantIdDiscordBot.RateLimiter
|
||||||
alias PlantIdDiscordBot.PlantNet.Parser
|
alias PlantIdDiscordBot.PlantNet.Parser
|
||||||
|
alias PlantIdDiscordBot.FileServer.File
|
||||||
|
|
||||||
@api Application.compile_env(:plantid_discord_bot, :api)
|
@api Application.compile_env(:plantid_discord_bot, :api)
|
||||||
|
|
||||||
@@ -26,18 +26,40 @@ defmodule PlantIdDiscordBot.Cog.PlantNet do
|
|||||||
{:ok, _} ->
|
{:ok, _} ->
|
||||||
do_identification(interaction)
|
do_identification(interaction)
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO increase on success (failed id or otherwise)
|
|
||||||
# RateLimiter.increase_counter(guild_id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_identification(interaction) do
|
defp do_identification(interaction) do
|
||||||
attachment_urls = get_attachment_urls(interaction)
|
attachment_urls = get_attachment_urls(interaction)
|
||||||
original_images = get_original_images(attachment_urls)
|
original_images = get_original_images(attachment_urls)
|
||||||
|
|
||||||
|
try do
|
||||||
|
Enum.each(attachment_urls, fn url ->
|
||||||
|
binary = File.download_file!(url)
|
||||||
|
File.save_file!(binary)
|
||||||
|
end)
|
||||||
|
rescue
|
||||||
|
e in RuntimeError ->
|
||||||
|
# Logger.error("An error occurred while processing the images.")
|
||||||
|
|
||||||
|
@api.create_interaction_response(interaction, %{
|
||||||
|
type: 4,
|
||||||
|
data: %{
|
||||||
|
content:
|
||||||
|
"An error occurred while processing the images." <> "\n\nReason:\n#{e.message}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO call PlantNet API and get response, with error handling
|
||||||
|
|
||||||
|
# TODO pass this response to the parser as below, with error handling
|
||||||
|
|
||||||
# temp data
|
# temp data
|
||||||
response_message = Parser.parse(@plantnet_raw_response)
|
response_message = Parser.parse(@plantnet_raw_response)
|
||||||
|
|
||||||
|
# TODO increase on success (failed id or otherwise)
|
||||||
|
# RateLimiter.increase_counter(guild_id)
|
||||||
|
|
||||||
@api.create_interaction_response(interaction, %{
|
@api.create_interaction_response(interaction, %{
|
||||||
type: 4,
|
type: 4,
|
||||||
data: %{content: response_message <> "\n#{original_images}"}
|
data: %{content: response_message <> "\n#{original_images}"}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
defmodule PlantIdDiscordBot.FileServer.File do
|
||||||
|
@moduledoc """
|
||||||
|
File utilities
|
||||||
|
"""
|
||||||
|
alias PlantIdDiscordBot.FileServer.ImageConverter
|
||||||
|
|
||||||
|
@image_path Application.compile_env(:plantid_discord_bot, :image_path)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Downloads a file from the given URL. Throws an error if unsuccessful.
|
||||||
|
"""
|
||||||
|
@spec download_file!(String.t()) :: binary
|
||||||
|
def download_file!(url) do
|
||||||
|
HTTPoison.get!(url)
|
||||||
|
|> Map.get(:body)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec save_file!(binary) :: :ok
|
||||||
|
def save_file!(binary) do
|
||||||
|
File.mkdir_p!(@image_path)
|
||||||
|
|
||||||
|
jpg_binary = ImageConverter.to_jpg!(binary)
|
||||||
|
|
||||||
|
generate_unique_filename("jpg")
|
||||||
|
|> File.write!(binary)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Generates a unique filename with the given file extension.
|
||||||
|
"""
|
||||||
|
@spec generate_unique_filename(String.t()) :: String.t()
|
||||||
|
def generate_unique_filename(file_extension) do
|
||||||
|
filename = :crypto.strong_rand_bytes(8) |> Base.encode16()
|
||||||
|
Path.join(@image_path, "#{filename}.#{file_extension}")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
defmodule PlantIdDiscordBot.FileServer.ImageConverter do
|
||||||
|
@moduledoc """
|
||||||
|
Image conversion utilities
|
||||||
|
"""
|
||||||
|
alias PlantIdDiscordBot.FileServer.MimeTypes
|
||||||
|
|
||||||
|
def to_jpg!(binary) do
|
||||||
|
case MimeTypes.detect_mime_type(binary) do
|
||||||
|
{:error, message} -> raise RuntimeError, message
|
||||||
|
"image/jpeg" -> binary
|
||||||
|
_ -> do_to_jpg(binary)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_to_jpg(binary) do
|
||||||
|
Image.from_binary!(binary)
|
||||||
|
|> Image.write!(:memory, suffix: ".jpg")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
defmodule PlantIdDiscordBot.FileServer.MimeTypes do
|
||||||
|
@moduledoc """
|
||||||
|
Mime type utilities
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Detect the mime type of a binary
|
||||||
|
"""
|
||||||
|
@spec detect_mime_type(binary) :: String.t()
|
||||||
|
def detect_mime_type(<<0x52, 0x49, 0x46, 0x46, _::binary>>), do: "image/webp"
|
||||||
|
def detect_mime_type(<<0xFF, 0xD8, 0xFF, _::binary>>), do: "image/jpeg"
|
||||||
|
def detect_mime_type(<<0x89, 0x50, 0x4E, 0x47, _::binary>>), do: "image/png"
|
||||||
|
def detect_mime_type(<<0x47, 0x49, 0x46, 0x38, _::binary>>), do: "image/gif"
|
||||||
|
def detect_mime_type(<<0x42, 0x4D, _::binary>>), do: "image/bmp"
|
||||||
|
def detect_mime_type(<<0x49, 0x49, 0x2A, 0x00, _::binary>>), do: "image/tiff"
|
||||||
|
def detect_mime_type(_), do: {:error, "Unsupported file type"}
|
||||||
|
end
|
||||||
@@ -36,14 +36,14 @@ defmodule PlantIdDiscordBot.PlantNet.Parser do
|
|||||||
@spec parse(String.t()) :: map()
|
@spec parse(String.t()) :: map()
|
||||||
def parse(response) do
|
def parse(response) do
|
||||||
response
|
response
|
||||||
|> to_map()
|
|> to_map!()
|
||||||
|> filter_by_score()
|
|> filter_by_score()
|
||||||
|> add_external_urls()
|
|> add_external_urls()
|
||||||
|> generate_response_message()
|
|> generate_response_message()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec to_map(String.t()) :: map()
|
@spec to_map!(String.t()) :: map()
|
||||||
def to_map(response), do: Jason.decode!(response)
|
def to_map!(response), do: Jason.decode!(response)
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Filters the data by score. Data is a parsed response from the PlantNet API.
|
Filters the data by score. Data is a parsed response from the PlantNet API.
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 219 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 273 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
defmodule PlantIdDiscordBotTest.FileServer.File do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest PlantIdDiscordBot.FileServer.File
|
||||||
|
|
||||||
|
@image_path Application.compile_env(:plantid_discord_bot, :image_path)
|
||||||
|
|
||||||
|
test "generate_unique_filename" do
|
||||||
|
file_path = PlantIdDiscordBot.FileServer.File.generate_unique_filename("jpg")
|
||||||
|
file_name = Path.basename(file_path)
|
||||||
|
assert Regex.match?(~r/^[0-9A-F]{16}\.jpg$/, file_name)
|
||||||
|
assert file_path == Path.join(@image_path, file_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -7,7 +7,7 @@ defmodule PlantIdDiscordBotTest.PlantNet.Parser do
|
|||||||
test "to_map" do
|
test "to_map" do
|
||||||
result =
|
result =
|
||||||
PlantNetFixtures.raw_response()
|
PlantNetFixtures.raw_response()
|
||||||
|> Parser.to_map()
|
|> Parser.to_map!()
|
||||||
|
|
||||||
assert result == PlantNetFixtures.parsed_response()
|
assert result == PlantNetFixtures.parsed_response()
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user