defmodule SApp.Chat do
use GenServer
def start_link(channel) do
GenServer.start_link(__MODULE__, channel)
end
def init(channel) do
{:ok, store} = SData.MerkleList.start_link(&msg_cmp/2)
manifest = {:chat, channel}
id = :crypto.hash(:sha256, :erlang.term_to_binary(manifest))
GenServer.cast(Shard.Manager, {:register, id, self()})
GenServer.cast(self(), :init_pull)
{:ok, %{channel: channel, id: id, manifest: manifest, store: store, peers: %{}}}
end
def handle_call(:manifest, _from, state) do
{:reply, state.manifest, state}
end
def handle_cast({:redundant, _}, _state) do
exit :normal
end
def handle_cast(:init_pull, state) do
GenServer.call(SNet.Manager, :get_all)
|> Enum.each(&(GenServer.cast(&1, {:send_msg, {:interested, [state.id]}})))
{:noreply, state}
end
def handle_cast({:chat_send, msg}, state) do
msgitem = {(System.os_time :seconds),
Shard.Identity.get_nickname(),
msg}
GenServer.cast(state.store, {:insert, msgitem})
for {_, pid} <- state.peers do
push_messages(state, pid, nil, 5)
end
{:noreply, state}
end
def handle_cast({:interested, peer_id, peer_pid}, state) do
push_messages(state, peer_pid, nil, 10)
new_peers = Map.put(state.peers, peer_id, peer_pid)
{:noreply, %{ state | peers: new_peers }}
end
def handle_cast({:msg, peer_id, peer_pid, msg}, state) do
case msg do
:get_top -> push_messages(peer_id, state, nil, 10)
{:get, start} -> push_messages(peer_id, state, start, 20)
{:info, _start, list, rest} ->
if rest != nil and not GenServer.call(state.store, {:has, rest}) do
GenServer.cast(peer_pid, {:send_msg, {state.id, {:get, rest}}})
end
spawn_link(fn ->
Process.sleep 1000
GenServer.cast(state.store, {:insert_many, list, (fn msg -> msg_callback(state.channel, msg) end)})
end)
end
if Map.has_key?(state.peers, peer_id) do
{:noreply, state}
else
handle_cast({:interested, peer_id, peer_pid}, state)
end
end
defp push_messages(state, to, start, num) do
case GenServer.call(state.store, {:read, start, num}) do
{:ok, list, rest} ->
GenServer.cast(to, {:send_msg, {state.id, {:info, start, list, rest}}})
_ -> nil
end
end
def msg_callback(chan, {ts, nick, msg}) do
IO.puts "#{ts |> DateTime.from_unix! |> DateTime.to_iso8601} ##{chan} <#{nick}> #{msg}"
end
def msg_cmp({ts1, nick1, msg1}, {ts2, nick2, msg2}) do
SData.MerkleList.cmp_ts_str({ts1, nick1<>"|"<>msg1},
{ts2, nick2<>"|"<>msg2})
end
end