defmodule Agala.Bot.Supervisor do use Supervisor # These default can be filled by some default parameters in the future @defaults [] @doc """ Retrieves the compile time configuration for the bot. """ @spec compile_config(:poller | :plug | :handler, bot :: Agala.Bot.t(), opts :: Keyword.t()) :: {atom, Agala.Provider.t(), Keyword.t()} def compile_config(:poller, bot, opts) do otp_app = Keyword.fetch!(opts, :otp_app) config = Application.get_env(otp_app, bot, []) config = Keyword.merge(opts, config) # Provider checking section provider = opts[:provider] || config[:provider] unless provider do raise ArgumentError, "missing :provider configuration in " <> "config #{inspect(otp_app)}, #{inspect(bot)}" end unless Code.ensure_compiled?(provider) do raise ArgumentError, "provider #{inspect(provider)} was not compiled, " <> "ensure it is correct and it is included as a project dependency" end # Handler checking section chain = opts[:chain] || config[:chain] unless chain do raise ArgumentError, "missing :chain configuration in " <> "config #{inspect(otp_app)}, #{inspect(bot)}" end unless Code.ensure_compiled?(chain) do raise ArgumentError, "chain #{inspect(chain)} was not compiled, " <> "ensure it is correct and it is included as a module in the project" end unless Agala.Chain in Agala.Util.behaviours_list(chain) do raise ArgumentError, "chain #{inspect(chain)} does not implement Agala.Chain behaviour, " <> "ensure it is correct and it is included as a module in the project" end {otp_app, provider, config} end @doc """ Retrieves the runtime configuration for the bot. """ @spec runtime_config( mode :: :poller | :plug | :handler, type :: :dry_run | :supervisor, bot :: Agala.Bot.t(), otp_app :: atom(), opts :: Keyword.t() ) :: {:ok, Keyword.t()} | :ignore def runtime_config(:poller, type, bot, otp_app, opts) do if config = Application.get_env(otp_app, bot, []) do config = [otp_app: otp_app, bot: bot] |> Keyword.merge(@defaults) |> Keyword.merge(config) |> Enum.into(%{}) |> Map.merge(opts) case bot_init(type, bot, config) do {:ok, config} -> {:ok, config} :ignore -> :ignore end else raise ArgumentError, "configuration for #{inspect(bot)} not specified in #{inspect(otp_app)} environment" end end ### Start section @spec start_link( mode :: :poller | :plug | :handler, bot :: Agala.Bot.t(), otp_app :: atom(), provider :: Agala.Provider.t(), opts :: Keyword.t() ) :: Supervisor.on_start() def start_link(:plug, bot, _, _, _) do raise RuntimeError, "Plugs should not be started as supervisors. Check out your #{inspect bot} plug" end def start_link(mode, bot, otp_app, provider, opts) do IO.inspect "Opts coming: #{inspect opts}" Supervisor.start_link(__MODULE__, {mode, bot, otp_app, provider, opts}, [name: bot]) end @spec bot_init(type :: :dry_run | :supervisor, bot :: Agala.Bot.t(), config :: Keyword.t()) :: {:ok, Keyword.t()} | :ignore defp bot_init(type, bot, config) do if Code.ensure_loaded?(bot) and function_exported?(bot, :init, 2) do bot.init(type, config) else {:ok, config} end end ## Callbacks @spec init({ mode :: :poller, :plug, :handler, bot :: Agala.Bot.t(), otp_app :: atom(), provider :: Agala.Provider.t(), opts :: Keyword.t() }) :: {:ok, tuple()} | :ignore def init({:poller, bot, otp_app, provider, opts}) do case runtime_config(:poller, :supervisor, bot, otp_app, opts) do {:ok, opts} -> children = [ {Agala.Bot.Storage, name: Module.concat(bot, Storage)}, {provider.get_bot(:poller), opts} ] Supervisor.init(children, strategy: :one_for_one) :ignore -> :ignore end end end