Finish removing Omnibot.State functions

Omnibot.State shouldn't be used anywhere anymore except as a GenServer
being started up in the supervisor.

Also, configuration must be loaded through Config.load/1 rather than
being constructed, because everything expects a tuple of {plugin,
config} now.

Finally, Omnibot.Core must be added to the configuration in order for
basic functionality.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-08-11 15:23:53 -07:00
parent 2165167954
commit 2f378dd438
7 changed files with 110 additions and 102 deletions

View File

@@ -87,8 +87,9 @@ defmodule Omnibot.Config do
""" """
def all_channels(cfg) do def all_channels(cfg) do
Enum.flat_map(cfg.plugins, fn Enum.flat_map(cfg.plugins, fn
{_, [channels: :all]} -> [] {_, cfg} -> if cfg[:channels] in [nil, :all],
{_, [channels: channels]} -> channels do: [],
else: cfg[:channels]
end) end)
|> MapSet.new() |> MapSet.new()
|> MapSet.to_list() |> MapSet.to_list()
@@ -108,9 +109,9 @@ defmodule Omnibot.Config do
%Omnibot.Irc.Msg.Prefix {:nick => "omnibot", :user => "omnibot", :host => nil} %Omnibot.Irc.Msg.Prefix {:nick => "omnibot", :user => "omnibot", :host => nil}
""" """
def msg_prefix(cfg) do def msg_prefix(cfg) do
%Msg.Prefix { %Msg.Prefix{
nick: cfg.nick, nick: cfg.nick,
user: cfg.user, user: cfg.user
} }
end end
@@ -135,10 +136,29 @@ defmodule Omnibot.Config do
} }
""" """
def msg(cfg, command, params \\ []) do def msg(cfg, command, params \\ []) do
%Msg { %Msg{
prefix: msg_prefix(cfg), prefix: msg_prefix(cfg),
command: command, command: command,
params: params, params: params
} }
end end
def channel_plugins(cfg, channel) do
cfg.plugins
|> Enum.filter(fn {_plug, cf} ->
cf[:channels] == :all or channel in Keyword.get(cf, :channels, [])
end)
end
def load(path) do
with {_, bindings} <- Code.eval_file(path) do
cfg = bindings[:config]
plugins = cfg.plugins
|> Enum.map(fn
plug when is_atom(plug) -> {plug, plug.default_config()}
{plug, cfg}-> {plug, cfg ++ plug.default_config()}
end)
%Omnibot.Config{cfg | plugins: plugins}
end
end
end end

View File

@@ -1,7 +1,7 @@
defmodule Omnibot.Irc do defmodule Omnibot.Irc do
require Logger require Logger
alias Omnibot.Irc.Msg alias Omnibot.Irc.Msg
alias Omnibot.{Config, State} alias Omnibot.Config
use GenServer use GenServer
## Client API ## Client API
@@ -29,8 +29,13 @@ defmodule Omnibot.Irc do
def cfg(irc), do: GenServer.call(irc, :cfg) def cfg(irc), do: GenServer.call(irc, :cfg)
defp route_msg(irc, msg) do defp route_msg(irc, cfg, msg) do
plugins = Msg.channel(msg) |> State.channel_plugins() # TODO :
# * Plugins that are loaded are not having their defaults applied, since
# the add_loaded_plugin in State would handle that. May be a good idea to
# make a Config.load/1 function that will handle loading of a
# configuration file instead of doing it in the root Omnibot module
plugins = Config.channel_plugins(cfg, Msg.channel(msg))
Task.Supervisor.async_stream_nolink( Task.Supervisor.async_stream_nolink(
Omnibot.RouterSupervisor, Omnibot.RouterSupervisor,
@@ -85,12 +90,12 @@ defmodule Omnibot.Irc do
end end
@impl true @impl true
def handle_info({:tcp, _socket, line}, state) do def handle_info({:tcp, _info_socket, line}, state = {_socket, cfg}) do
Logger.debug(String.trim(line)) Logger.debug(String.trim(line))
msg = Msg.parse(line) msg = Msg.parse(line)
# Send the message to the router # Send the message to the router
route_msg(self(), msg) route_msg(self(), cfg, msg)
{:noreply, state} {:noreply, state}
end end

View File

@@ -3,7 +3,6 @@ defmodule Omnibot.PluginManager do
use Supervisor use Supervisor
require Logger require Logger
alias Omnibot.State
def start_link(opts \\ []) do def start_link(opts \\ []) do
Supervisor.start_link(__MODULE__, opts[:cfg], opts) Supervisor.start_link(__MODULE__, opts[:cfg], opts)
@@ -13,23 +12,10 @@ defmodule Omnibot.PluginManager do
def init(cfg) do def init(cfg) do
compile_files(cfg.plugin_paths || []) compile_files(cfg.plugin_paths || [])
# These are plugins that need to be loaded for core functionality of the bot
core = [
Omnibot.Core,
]
# 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 {plugin, cfg} <- cfg.plugins,
{name, cfg} = case plug do do: {Omnibot.Plugin.Supervisor, plugin: plugin, cfg: cfg}
{name, cfg} -> {name, cfg ++ name.default_config()}
name -> {name, name.default_config()}
end
{Omnibot.Plugin.Supervisor, plugin: name, cfg: cfg}
end
# Add each child to the "loaded plugins" list in the State
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

View File

@@ -3,6 +3,7 @@ defmodule Omnibot.Supervisor do
use Supervisor use Supervisor
require IEx require IEx
alias Omnibot.Config
def start_link(opts) do def start_link(opts) do
Supervisor.start_link(__MODULE__, :ok, opts) Supervisor.start_link(__MODULE__, :ok, opts)
@@ -10,10 +11,7 @@ defmodule Omnibot.Supervisor do
@impl true @impl true
def init(:ok) do def init(:ok) do
cfg = System.get_env("OMNIBOT_CFG", "omnibot.exs") |> Config.load()
{_, bindings} = System.get_env("OMNIBOT_CFG", "omnibot.exs")
|> Code.eval_file()
cfg = bindings[:config]
# TODO : move cfg to its own process so reloading it is as simple as killing the process # TODO : move cfg to its own process so reloading it is as simple as killing the process
children = [ children = [

View File

@@ -7,6 +7,7 @@ config = %Config {
ssl: false, ssl: false,
plugins: [ plugins: [
Omnibot.Core,
{Omnibot.Contrib.OnConnect, commands: [ {Omnibot.Contrib.OnConnect, commands: [
["privmsg", "nickserv", "register", "password123", "omnibot@omni.bot"], ["privmsg", "nickserv", "register", "password123", "omnibot@omni.bot"],
["privmsg", "nickserv", "identify", "password123"] ["privmsg", "nickserv", "identify", "password123"]

63
test/config_test.exs Normal file
View File

@@ -0,0 +1,63 @@
defmodule Omnibot.ConfigTest do
use ExUnit.Case, async: true
doctest Omnibot.Config
alias Omnibot.Config
test "channel_plugins works correctly" do
cfg = %Config {
server: "test",
plugins: [
{FooBar, channels: ["#foo", "#bar"]},
{Foo, channels: ["#foo"]},
{Bar, channels: ["#bar"]},
{Baz, channels: ["#baz"]},
{All, channels: :all},
]
}
plugins = Config.channel_plugins(cfg, "#foo")
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 3
assert Enum.member?(plugins, FooBar)
assert Enum.member?(plugins, Foo)
assert Enum.member?(plugins, All)
plugins = Config.channel_plugins(cfg, "#bar")
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 3
assert Enum.member?(plugins, FooBar)
assert Enum.member?(plugins, Bar)
assert Enum.member?(plugins, All)
plugins = Config.channel_plugins(cfg, "#baz")
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 2
assert Enum.member?(plugins, Baz)
assert Enum.member?(plugins, All)
plugins = Config.channel_plugins(cfg, nil)
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 1
assert Enum.member?(plugins, All)
end
test "all_channels works correctly" do
cfg = %Config {
server: "testing",
plugins: [
{FooBar, channels: ["#foo", "#bar"]},
{Foo, channels: ["#foo"]},
{Bar, channels: ["#bar"]},
{Baz, channels: ["#baz"]},
{All, channels: :all},
],
}
channels = Config.all_channels(cfg)
assert length(channels) == 3
assert Enum.member?(channels, "#foo")
assert Enum.member?(channels, "#bar")
assert Enum.member?(channels, "#baz")
end
end

View File

@@ -1,65 +0,0 @@
defmodule StateTest do
use ExUnit.Case
alias Omnibot.State
setup do
state = start_supervised!(State)
{:ok, state: state}
end
test "state channel_plugins works correctly", %{state: state} do
plugins = [
{FooBar, channels: ["#foo", "#bar"]},
{Foo, channels: ["#foo"]},
{Bar, channels: ["#bar"]},
{Baz, channels: ["#baz"]},
{All, channels: :all},
]
plugins |> Enum.each(fn plugin -> State.add_loaded_plugin(state, plugin) end)
plugins = State.channel_plugins(state, "#foo")
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 3
assert Enum.member?(plugins, FooBar)
assert Enum.member?(plugins, Foo)
assert Enum.member?(plugins, All)
plugins = State.channel_plugins(state, "#bar")
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 3
assert Enum.member?(plugins, FooBar)
assert Enum.member?(plugins, Bar)
assert Enum.member?(plugins, All)
plugins = State.channel_plugins(state, "#baz")
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 2
assert Enum.member?(plugins, Baz)
assert Enum.member?(plugins, All)
plugins = State.channel_plugins(state, nil)
|> Enum.map(fn {plugin, _} -> plugin end)
assert length(plugins) == 1
assert Enum.member?(plugins, All)
end
test "state all_channels works correctly", %{state: state} do
plugins = [
{FooBar, channels: ["#foo", "#bar"]},
{Foo, channels: ["#foo"]},
{Bar, channels: ["#bar"]},
{Baz, channels: ["#baz"]},
{All, channels: :all},
]
plugins |> Enum.each(fn plugin -> State.add_loaded_plugin(state, plugin) end)
channels = State.all_channels(state)
assert length(channels) == 3
assert Enum.member?(channels, "#foo")
assert Enum.member?(channels, "#bar")
assert Enum.member?(channels, "#baz")
end
end