Files
omnibot/lib/contrib/markov/chain_server.ex
2020-07-17 20:51:01 -07:00

126 lines
3.1 KiB
Elixir

defmodule Omnibot.Contrib.Markov.ChainServer do
use GenServer
alias Omnibot.Contrib.Markov
require Logger
## Client API
def start_link(opts) do
{channel, opts} = Keyword.pop(opts, :channel)
{user, opts} = Keyword.pop(opts, :user)
GenServer.start_link(__MODULE__, {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) when user != :all 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
def generate(server) do
GenServer.call(server, :generate)
end
def chain_sum(server) do
GenServer.call(server, :chain_sum)
end
## Server callbacks
@impl true
def init({channel, :all}) do
Logger.debug("Creating allchain for channel #{channel}")
chain = File.ls!(channel_dir(channel))
|> Enum.map(&(Path.join(channel_dir(channel), &1) |> Markov.Chain.load!()))
|> Markov.Chain.merge()
{:ok, {chain, channel, :all}}
# TODO: load allchain
#chain = case load(channel, user) do
#{:ok, chain} -> chain
#{:error, _} -> %Markov.Chain{order: cfg()[:order]}
#end
#{:ok, {chain, channel, user}}
end
@impl true
def init({channel, user}) do
chain = case load(channel, user) do
{:ok, chain} -> chain
{:error, _} -> %Markov.Chain{order: Markov.cfg()[:order]}
end
{:ok, {chain, channel, user}}
end
@impl true
def handle_call(:save, _from, state = {_chain, channel, :all}) do
Logger.debug("Not saving :all chain for #{channel}")
{:reply, :ok, state}
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}")
:ok = Markov.Chain.save!(chain, path)
{: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
@impl true
def handle_call(:generate, _from, state = {chain, _channel, _user}) do
{:reply, Markov.Chain.generate(chain), state}
end
@impl true
def handle_call(:chain_sum, _from, state = {chain, _channel, _user}) do
{:reply, Markov.Chain.chain_sum(chain), state}
end
end