From b234a360dafa0a65d797614aad5a4514570784f8 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 4 Jul 2018 13:17:26 +0200 Subject: Multiple chat rooms (no web UI yet) --- lib/app/chat.ex | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 7 deletions(-) (limited to 'lib/app') diff --git a/lib/app/chat.ex b/lib/app/chat.ex index 4a56085..f165514 100644 --- a/lib/app/chat.ex +++ b/lib/app/chat.ex @@ -1,17 +1,86 @@ defmodule SApp.Chat do - def send(msg) 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(SApp.Chat.Log, {:insert, msgitem}) + GenServer.cast(state.store, {:insert, msgitem}) + + for {_, pid} <- state.peers do + push_messages(state, pid, nil, 5) + end - SNet.ConnSupervisor - |> DynamicSupervisor.which_children - |> Enum.each(fn {_, pid, _, _} -> GenServer.cast(pid, :init_push) end) + {:noreply, state} end - def msg_callback({ts, nick, msg}) do - IO.puts "#{ts |> DateTime.from_unix! |> DateTime.to_iso8601} <#{nick}> #{msg}" + 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 -- cgit v1.2.3