Packages

Easier creation of the Visual Novel-like games or presentations.

Current section

Files

Jump to
dev_joy lib dev_joy forum_data.ex
Raw

lib/dev_joy/forum_data.ex

defmodule DevJoy.ForumData do
@moduledoc """
An example character's data module which fetches forum data using username and Discourse API.
It uses only publicly available data which does not requires any authentication.
## Configuration
There are 2 simple steps.
Firstly instruct a character configuration to use this module.
Secondly configure this module to use your desired endpoint.
config :dev_joy, DevJoy.Character, data_module: #{inspect(__MODULE__)}
config :dev_joy, #{inspect(__MODULE__)}, endpoint: "https://meta.discourse.org"
"""
@behaviour DevJoy.Character
alias DevJoy.Character
alias DevJoy.Scene
@avatar_size 48
@ets_table_name :__dev_joy_forum_data_cache__
@doc false
@spec ets_table_name :: unquote(@ets_table_name)
def ets_table_name, do: @ets_table_name
@impl DevJoy.Character
def get_character(id) do
ets_create_named_table_if_does_not_exists()
id
|> ets_get_forum_data()
|> then(&(&1 || fetch_forum_data(id)))
end
@spec ets_create_named_table_if_does_not_exists :: unquote(@ets_table_name) | nil
defp ets_create_named_table_if_does_not_exists do
if :ets.whereis(@ets_table_name) == :undefined do
:ets.new(@ets_table_name, [:set, :protected, :named_table])
end
end
@spec ets_get_forum_data(Character.id()) :: Scene.struct_fields() | nil
defp ets_get_forum_data(id) do
case :ets.lookup(@ets_table_name, id) do
[] -> nil
[character] -> character
end
end
@spec fetch_forum_data(Character.id()) :: Scene.struct_fields()
defp fetch_forum_data(id) do
{:ok, _} = Application.ensure_all_started(:req)
:dev_joy
|> Application.get_env(__MODULE__, endpoint: "https://elixirforum.com")
|> Keyword.fetch!(:endpoint)
|> Path.join("u/#{id}.json")
|> Req.get!()
|> then(& &1.body["user"])
|> then(&[avatar: avatar_from_template(&1["avatar_template"]), full_name: &1["name"]])
|> tap(&ets_cache_forum_data(id, &1))
end
@spec avatar_from_template(String.t()) :: Character.avatar()
defp avatar_from_template(template) do
template
|> String.replace("{size}", Integer.to_string(@avatar_size))
|> then(&Path.join("elixirforum.com", &1))
end
@spec ets_cache_forum_data(Character.id(), Scene.struct_fields()) :: true
defp ets_cache_forum_data(id, character) do
:ets.insert(@ets_table_name, {id, character})
end
end