defmodule IRC.Plugin do require Logger defmodule Supervisor do use DynamicSupervisor require Logger def start_link() do DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__) end def start_child(module, opts \\ []) do Logger.info("Starting #{module}") spec = %{id: {IRC.Plugin,module}, start: {IRC.Plugin, :start_link, [module, opts]}, name: module, restart: :transient} case DynamicSupervisor.start_child(__MODULE__, spec) do {:ok, _} = res -> res :ignore -> Logger.warn("Ignored #{module}") :ignore {:error,_} = res -> Logger.error("Could not start #{module}: #{inspect(res, pretty: true)}") res end end @impl true def init(_init_arg) do DynamicSupervisor.init( strategy: :one_for_one, max_restarts: 10, max_seconds: 1 ) end end def dets(), do: to_charlist(Nola.data_path("/plugins.dets")) def setup() do :dets.open_file(dets(), []) end def enabled() do :dets.foldl(fn {name, true, _}, acc -> [name | acc] _, acc -> acc end, [], dets()) end def start_all() do for mod <- enabled(), do: {mod, IRC.Plugin.Supervisor.start_child(mod)} end def declare(module) do case get(module) do :disabled -> :dets.insert(dets(), {module, true, nil}) _ -> nil end end def start(module, opts \\ []) do IRC.Plugin.Supervisor.start_child(module) end @doc "Enables a plugin" def enable(name), do: switch(name, true) @doc "Disables a plugin" def disable(name), do: switch(name, false) @doc "Enables or disables a plugin" def switch(name, value) when is_boolean(value) do last = case get(name) do {:ok, last} -> last _ -> nil end :dets.insert(dets(), {name, value, last}) end @spec get(module()) :: {:ok, last_start :: nil | non_neg_integer()} | :disabled def get(name) do case :dets.lookup(dets(), name) do [{name, enabled, last_start}] -> {:ok, enabled, last_start} _ -> :disabled end end def start_link(module, options \\ []) do with {:disabled, {_, true, last}} <- {:disabled, get(module)}, {:throttled, false} <- {:throttled, false} do module.start_link() else {error, _} -> Logger.info("Plugin: #{to_string(module)} ignored start: #{to_string(error)}") :ignore end end end