87 lines
2.1 KiB
Elixir
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
|