Files
omnibot/lib/contrib/markov/chain_server.ex
Alek Ratzloff 6f625c21ca Add Markov.ChainServer for Markov plugin
* Markov chains each have their own process keeping it updated and
  synchronized. The markov plugin has been upgraded to account for this.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2020-07-17 17:40:22 -07:00

87 lines
2.1 KiB
Elixir

defmodule Omnibot.Contrib.Markov.ChainServer do
use GenServer
alias Omnibot.Contrib.Markov
require Logger
## Client API
def start_link(opts) do
{cfg, opts} = Keyword.pop(opts, :cfg)
{channel, opts} = Keyword.pop(opts, :channel)
{user, opts} = Keyword.pop(opts, :user)
chain = case load(channel, user) do
{:ok, chain} -> chain
{:error, _} -> %Markov.Chain{order: cfg[:order]}
end
GenServer.start_link(__MODULE__, {chain, channel, user}, opts)
end
@compile :inline
def user_path(channel, user), do: Path.join(channel_dir(channel), "#{user}.chain")
@compile :inline
def channel_dir(channel), do: Path.join(Markov.save_dir(), channel)
def load(channel, user) do
with {:ok, contents} <- user_path(channel, user) |> File.read(),
do: {:ok, :erlang.binary_to_term(contents)}
end
def save(server) do
GenServer.call(server, :save)
end
def train(server, msg) do
GenServer.call(server, {:train, msg})
end
def chain(server) do
GenServer.call(server, :chain)
end
def channel(server) do
GenServer.call(server, :channel)
end
def user(server) do
GenServer.call(server, :user)
end
## Server callbacks
@impl true
def init({chain, channel, user}) do
{:ok, {chain, channel, user}}
end
@impl true
def handle_call(:save, _from, state = {chain, channel, user}) do
File.mkdir_p!(channel_dir(channel))
path = user_path(channel, user)
Logger.debug("Saving chain for #{user} on #{channel} to #{path}")
File.write!(path, :erlang.term_to_binary(chain))
{:reply, :ok, state}
end
@impl true
def handle_call({:train, msg}, _from, {chain, channel, user}) do
{:reply, :ok, {Markov.Chain.train(chain, msg), channel, user}}
end
@impl true
def handle_call(:chain, _from, state = {chain, _channel, _user}) do
{:reply, chain, state}
end
@impl true
def handle_call(:channel, _from, state = {_chain, channel, _user}) do
{:reply, channel, state}
end
@impl true
def handle_call(:user, _from, state = {_chain, _channel, user}) do
{:reply, user, state}
end
end