Finish plugin and routing overhaul, there's a new model in town:
* Plugins all derive from Omnibot.Plugin. There still is a base plugin, in case we want to have another plugin backend instead of a GenServer * All plugins are monitored by a unique Plugin.Supervisor, which is in turn started by the PluginSupervisor (yes this is confusing, yes it needs to be renamed) * Any other auxiliary child processes may be started through the Plugin.children/1 function. * By default, plugins have a CfgState process which is an Agent that keeps track of the plugin's configuration and state * Plugin API is now called through the GenServer backend for better synchronicity. * Very few changes to the front-facing Plugin API, which is nice Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -6,7 +6,7 @@ defmodule Omnibot.Contrib.Markov do
|
|||||||
@default_config path: "markov", order: 2, save_every: 5 * 60
|
@default_config path: "markov", order: 2, save_every: 5 * 60
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def children(cfg, _state) do
|
def children(cfg) do
|
||||||
[{Task, fn ->
|
[{Task, fn ->
|
||||||
Stream.timer(cfg[:save_every] * 1000)
|
Stream.timer(cfg[:save_every] * 1000)
|
||||||
|> Stream.cycle()
|
|> Stream.cycle()
|
||||||
|
|||||||
@@ -5,23 +5,8 @@ defmodule Omnibot.Contrib.Wordbot do
|
|||||||
|
|
||||||
@default_config wordbot_source: "words.txt", wordbot_db: "wordbot.db", words_per_round: 300, hours_per_round: 5
|
@default_config wordbot_source: "words.txt", wordbot_db: "wordbot.db", words_per_round: 300, hours_per_round: 5
|
||||||
|
|
||||||
#def start_link(opts) do
|
|
||||||
#Supervisor.start_link(__MODULE__, opts[:cfg], opts)
|
|
||||||
#end
|
|
||||||
|
|
||||||
#@impl true
|
|
||||||
#def init(cfg) do
|
|
||||||
# children = [
|
|
||||||
# {Task.Supervisor, name: Omnibot.Contrib.Wordbot.Watchers, strategy: :one_for_one},
|
|
||||||
# Wordbot.Db.child_spec(cfg[:wordbot_db]),
|
|
||||||
# {Wordbot.Bot, cfg: cfg, name: Omnibot.Contrib.Wordbot.Bot},
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# Supervisor.init(children, strategy: :one_for_all)
|
|
||||||
#end
|
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def children(cfg, _state) do
|
def children(cfg) do
|
||||||
[
|
[
|
||||||
{Task.Supervisor, name: Omnibot.Contrib.Wordbot.Watchers, strategy: :one_for_one},
|
{Task.Supervisor, name: Omnibot.Contrib.Wordbot.Watchers, strategy: :one_for_one},
|
||||||
Wordbot.Db.child_spec(cfg[:wordbot_db]),
|
Wordbot.Db.child_spec(cfg[:wordbot_db]),
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ defmodule Omnibot.Plugin.Base do
|
|||||||
@impl true
|
@impl true
|
||||||
def on_init(_cfg), do: nil
|
def on_init(_cfg), do: nil
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def on_msg(irc, msg) do
|
||||||
|
route_msg(irc, msg)
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def default_config(), do: @default_config
|
def default_config(), do: @default_config
|
||||||
|
|
||||||
@@ -33,11 +38,6 @@ defmodule Omnibot.Plugin.Base do
|
|||||||
|
|
||||||
@behaviour Plugin.Base
|
@behaviour Plugin.Base
|
||||||
|
|
||||||
@impl Plugin.Base
|
|
||||||
def on_msg(irc, msg) do
|
|
||||||
route_msg(irc, msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp route_msg(irc, %Irc.Msg {prefix: nil}), do: nil
|
defp route_msg(irc, %Irc.Msg {prefix: nil}), do: nil
|
||||||
|
|
||||||
defp route_msg(irc, msg) do
|
defp route_msg(irc, msg) do
|
||||||
|
|||||||
@@ -14,21 +14,24 @@ defmodule Omnibot.Plugin do
|
|||||||
|
|
||||||
def state(pid), do: Agent.get(pid, fn {_, state} -> state end)
|
def state(pid), do: Agent.get(pid, fn {_, state} -> state end)
|
||||||
|
|
||||||
def update_state(pid, fun, timeout \\ 5000),
|
def update_state(pid, fun, timeout \\ 5000) do
|
||||||
do: Agent.update(pid, &{&1, apply(fun, [&1])}, timeout)
|
Agent.update(pid, fn {cfg, state} -> {cfg, apply(fun, [state])} end, timeout)
|
||||||
|
#Agent.update(pid, &{&1, apply(fun, [&1])}, timeout)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defmacro __using__(opts) do
|
defmacro __using__(opts) do
|
||||||
opts = opts ++ @default_opts
|
opts = opts ++ @default_opts
|
||||||
|
|
||||||
quote do
|
quote generated: true do
|
||||||
use Supervisor
|
use GenServer
|
||||||
use Omnibot.Plugin.Base
|
use Omnibot.Plugin.Base
|
||||||
|
|
||||||
## Client API
|
## Client API
|
||||||
|
|
||||||
def start_link(opts) do
|
def start_link(opts) do
|
||||||
Supervisor.start_link(__MODULE__, {opts[:cfg], opts[:state]}, opts)
|
{cfg, opts} = Keyword.pop(opts, :cfg)
|
||||||
|
GenServer.start_link(__MODULE__, cfg, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cfg() do
|
def cfg() do
|
||||||
@@ -48,30 +51,37 @@ defmodule Omnibot.Plugin do
|
|||||||
GenServer.cast(__MODULE__, {:handle_msg, irc, msg})
|
GenServer.cast(__MODULE__, {:handle_msg, irc, msg})
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Omnibot.Plugin
|
|
||||||
def children(_cfg, _state), do: []
|
|
||||||
|
|
||||||
## Server callbacks
|
## Server callbacks
|
||||||
|
|
||||||
@impl Supervisor
|
@impl GenServer
|
||||||
def init({cfg, state}) do
|
def init(_cfg) do
|
||||||
base_children = [
|
{:ok, nil}
|
||||||
{Omnibot.Plugin.CfgState, cfg: cfg, state: state, name: __MODULE__.CfgState},
|
|
||||||
]
|
|
||||||
children =
|
|
||||||
(if unquote(opts[:include_base]), do: base_children, else: []) ++ children(cfg, state)
|
|
||||||
Supervisor.init(children, unquote(opts[:opts]))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
def handle_cast({:handle_msg, irc, msg}, state) do
|
def handle_cast({:handle_msg, irc, msg}, state) do
|
||||||
on_msg(irc, msg)
|
on_msg(irc, msg)
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp base_children(cfg, state) when unquote(opts[:include_base]) do
|
||||||
|
[
|
||||||
|
{Omnibot.Plugin.CfgState, cfg: cfg, state: state, name: __MODULE__.CfgState},
|
||||||
|
{__MODULE__, name: __MODULE__},
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp base_children(_cfg, _state), do: []
|
||||||
|
|
||||||
|
@impl Omnibot.Plugin
|
||||||
|
def children(cfg), do: []
|
||||||
|
|
||||||
|
def plugin_children(cfg, state), do: base_children(cfg, state) ++ children(cfg)
|
||||||
|
|
||||||
@behaviour Omnibot.Plugin
|
@behaviour Omnibot.Plugin
|
||||||
defoverridable Omnibot.Plugin
|
defoverridable Omnibot.Plugin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@callback children(cfg :: [atom: any], state :: any) :: [atom | {atom, [atom: any]} | {atom, any, [atom: any]}]
|
@callback children(cfg :: [atom: any]) :: [atom | {atom, [atom: any]} | {atom, any, [atom: any]}]
|
||||||
end
|
end
|
||||||
|
|||||||
28
lib/plugin/supervisor.ex
Normal file
28
lib/plugin/supervisor.ex
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
defmodule Omnibot.Plugin.Supervisor do
|
||||||
|
use Supervisor
|
||||||
|
|
||||||
|
def start_link(opts) do
|
||||||
|
{plugin, opts} = Keyword.pop(opts, :plugin)
|
||||||
|
{cfg, opts} = Keyword.pop(opts, :cfg)
|
||||||
|
start_link(plugin, cfg, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_link(plugin, cfg, opts) when is_atom(plugin) do
|
||||||
|
Supervisor.start_link(__MODULE__, {plugin, cfg}, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def child_spec(arg) do
|
||||||
|
id = Module.concat(arg[:plugin], Plugin.Supervisor)
|
||||||
|
%{
|
||||||
|
id: id,
|
||||||
|
start: {__MODULE__, :start_link, [arg]},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init({plugin, cfg}) when is_atom(plugin) do
|
||||||
|
state = plugin.on_init(cfg)
|
||||||
|
children = IO.inspect(plugin.plugin_children(cfg, state))
|
||||||
|
Supervisor.init(children, strategy: :one_for_one)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -21,14 +21,15 @@ defmodule Omnibot.PluginSupervisor do
|
|||||||
# Map the plugins in the configuration to the children
|
# Map the plugins in the configuration to the children
|
||||||
children =
|
children =
|
||||||
for plug <- (core ++ cfg.plugins) do
|
for plug <- (core ++ cfg.plugins) do
|
||||||
case plug do
|
{name, cfg} = case plug do
|
||||||
{name, cfg} -> {name, cfg: cfg ++ name.default_config(), name: name}
|
{name, cfg} -> {name, cfg ++ name.default_config()}
|
||||||
name -> {name, cfg: name.default_config(), name: name}
|
name -> {name, name.default_config()}
|
||||||
end
|
end
|
||||||
|
{Omnibot.Plugin.Supervisor, plugin: name, cfg: cfg}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add each child to the "loaded plugins" list in the State
|
# Add each child to the "loaded plugins" list in the State
|
||||||
Enum.each(children, fn {plugin, opts} -> State.add_loaded_plugin({plugin, opts[:cfg]}) end)
|
Enum.each(children, fn {_plugin, opts} -> State.add_loaded_plugin({opts[:plugin], opts[:cfg]}) end)
|
||||||
|
|
||||||
Supervisor.init(children, strategy: :one_for_one)
|
Supervisor.init(children, strategy: :one_for_one)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user