From 4000528d81d3efebd6cfdfe2aab4697d1bd4d342 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Sat, 13 Jun 2020 18:45:02 -0400 Subject: [PATCH] Move more stuff from other places into Omnibot.Core * Present rooms are tracked by Omnibot.Core now, instead of Omnibot.State * Channel synchronization is done through Omnibot.Core instead of Omnibot.Irc * Add Omnibot.Module.on_init/1 callback, which returns a "state" value for the module to keep track of. By default, the state is nil. Signed-off-by: Alek Ratzloff --- lib/core.ex | 43 ++++++++++++++++++++++++++++++++++++------- lib/irc.ex | 18 ------------------ lib/module.ex | 29 +++++++++++++++++++++++------ lib/state.ex | 39 --------------------------------------- 4 files changed, 59 insertions(+), 70 deletions(-) diff --git a/lib/core.ex b/lib/core.ex index 4ba9bb0..891b65b 100644 --- a/lib/core.ex +++ b/lib/core.ex @@ -2,42 +2,71 @@ defmodule Omnibot.Core do use Omnibot.Module alias Omnibot.State + @impl true def on_join(irc, channel, nick) do cfg = State.cfg() if nick == cfg.nick do - State.add_channel(channel) + add_channel(channel) # Sync if we join a channel we shouldn't be in if !Enum.member?(State.all_channels(), channel), - do: Irc.sync_channels(irc) + do: sync_channels(irc) end end + @impl true def on_part(irc, channel, nick) do cfg = State.cfg() if nick == cfg.nick do - State.remove_channel(channel) + remove_channel(channel) # Sync if we join a channel we forcibly part a channel we shouldn't leave if Enum.member?(State.all_channels(), channel), - do: Irc.sync_channels(irc) + do: sync_channels(irc) end end + @impl true def on_kick(irc, channel, _nick, target) do cfg = State.cfg() if target == cfg.nick do - State.remove_channel(channel) + remove_channel(channel) # Generally, being kicked is not intentionally leaving a channel, so always sync here - Irc.sync_channels(irc) + sync_channels(irc) end end @impl true def on_msg(irc, msg) do case String.upcase(msg.command) do - "001" -> Irc.sync_channels(irc) + "001" -> sync_channels(irc) "PING" -> Irc.send_msg(irc, "PONG", msg.params) _ -> route_msg(irc, msg) end end + + @impl true + def on_init(_cfg) do + MapSet.new() + end + + defp sync_channels(irc) do + desired = MapSet.new(State.all_channels()) + present = state() + + to_join = MapSet.difference(desired, present) + |> MapSet.to_list() + to_part = MapSet.difference(present, desired) + |> MapSet.to_list() + + Enum.each(to_join, fn channel -> Irc.join(irc, channel) end) + Enum.each(to_part, fn channel -> Irc.part(irc, channel) end) + end + + defp add_channel(channel) do + update_state(fn state -> MapSet.put(state, channel) end) + end + + defp remove_channel(channel) do + update_state(fn state -> MapSet.delete(state, channel) end) + end end diff --git a/lib/irc.ex b/lib/irc.ex index c138209..8a9b3f8 100644 --- a/lib/irc.ex +++ b/lib/irc.ex @@ -28,8 +28,6 @@ defmodule Omnibot.Irc do def part(irc, channel), do: send_msg(irc, "PART", channel) - def sync_channels(irc), do: GenServer.cast(irc, :sync_channels) - defp route_msg(irc, msg) do channel = Msg.channel(msg) State.channel_modules(channel) @@ -72,22 +70,6 @@ defmodule Omnibot.Irc do {:noreply, socket} end - @impl true - def handle_cast(:sync_channels, socket) do - cfg = State.cfg() - desired = MapSet.new(Config.all_channels(cfg)) - present = MapSet.new(State.channels()) - to_join = MapSet.difference(desired, present) - |> MapSet.to_list() - to_part = MapSet.difference(present, desired) - |> MapSet.to_list() - - Enum.each(to_join, fn channel -> join(self(), channel) end) - Enum.each(to_part, fn channel -> part(self(), channel) end) - - {:noreply, socket} - end - @impl true def handle_info({:tcp, _socket, line}, socket) do Logger.debug(String.trim(line)) diff --git a/lib/module.ex b/lib/module.ex index c72db29..20f032d 100644 --- a/lib/module.ex +++ b/lib/module.ex @@ -15,7 +15,10 @@ defmodule Omnibot.Module do def on_part(_irc, _channel, _nick), do: nil @impl true - def on_kick(_irc, _channel, _nick), do: nil + def on_kick(_irc, _channel, _nick, _target), do: nil + + @impl true + def on_init(_cfg), do: nil end end end @@ -30,11 +33,24 @@ defmodule Omnibot.Module do @behaviour Module def start_link(opts) do - Agent.start_link(fn -> opts[:cfg] end, opts ++ [name: __MODULE__]) + cfg = opts[:cfg] + Agent.start_link(fn -> {cfg, on_init(cfg)} end, opts ++ [name: __MODULE__]) end def cfg do - Agent.get(__MODULE__, & &1) + Agent.get(__MODULE__, fn {cfg, _} -> cfg end) + end + + def state do + Agent.get(__MODULE__, fn {_, state} -> state end) + end + + def update_state(update, timeout \\ 5000) do + Agent.update( + __MODULE__, + fn {cfg, state} -> {cfg, apply(update, [state])} end, + timeout + ) end @impl Module @@ -66,8 +82,8 @@ defmodule Omnibot.Module do on_part(irc, channel, nick) "KICK" -> - [channel | _] = msg.params - on_kick(irc, channel, nick) + [channel, target | _] = msg.params + on_kick(irc, channel, nick, target) _ -> nil @@ -91,7 +107,8 @@ defmodule Omnibot.Module do ) :: any @callback on_join(irc :: pid(), channel :: String.t(), nick :: String.t()) :: any @callback on_part(irc :: pid(), channel :: String.t(), nick :: String.t()) :: any - @callback on_kick(irc :: pid(), channel :: String.t(), nick :: String.t()) :: any + @callback on_kick(irc :: pid(), channel :: String.t(), nick :: String.t(), target :: String.t()) :: any + @callback on_init(cfg :: any) :: any defmacro command(cmd, opts) do quote generated: true do diff --git a/lib/state.ex b/lib/state.ex index 361af6b..c57d503 100644 --- a/lib/state.ex +++ b/lib/state.ex @@ -36,30 +36,6 @@ defmodule Omnibot.State do @doc "Gets all loaded modules from the given state." def loaded_modules(state), do: GenServer.call(state, :loaded_modules) - @doc "Gets all channels that the bot is present in from the default State process." - def channels(), do: channels(__MODULE__) - - @doc "Gets all channels that the bot is present in from the given State process." - def channels(state) do - GenServer.call(state, :channels) - end - - @doc "Adds a channel to the list of joined channels of the default State process, if it is not already present." - def add_channel(channel), do: add_channel(__MODULE__, channel) - - @doc "Adds a channel to the list of joined channels of the given State process, if it is not already present." - def add_channel(state, channel) do - GenServer.cast(state, {:add_channel, channel}) - end - - @doc "Removes a channel from the list of joined channels of the default State process, if it exists." - def remove_channel(channel), do: remove_channel(__MODULE__, channel) - - @doc "Removes a channel from the list of joined channels of the given State process, if it exists." - def remove_channel(state, channel) do - GenServer.cast(state, {:remove_channel, channel}) - end - def all_channels(), do: all_channels(__MODULE__) def all_channels(state) do @@ -100,11 +76,6 @@ defmodule Omnibot.State do {:reply, state.cfg, state} end - @impl true - def handle_call(:channels, _from, state) do - {:reply, state.channels, state} - end - @impl true def handle_call(:loaded_modules, _from, state) do {:reply, state.module_map, state} @@ -115,14 +86,4 @@ defmodule Omnibot.State do state = %{state | module_map: Map.put(state.module_map, module, cfg)} {:noreply, state} end - - @impl true - def handle_cast({:add_channel, channel}, state) do - {:noreply, %{state | channels: state.channels |> MapSet.put(channel)}} - end - - @impl true - def handle_cast({:remove_channel, channel}, state) do - {:noreply, %{state | channels: state.channels |> MapSet.delete(channel)}} - end end