defmodule SCLI do @moduledoc """ Small command line interface for the chat application """ defmodule State do defstruct [:room_pid, :id_pid, :pk] end def run() do for {_chid, %SApp.Chat.Manifest{}, chpid} <- Shard.Manager.list_shards do GenServer.cast(chpid, {:subscribe, self()}) end pk = Shard.Keys.get_any_identity run(%State{room_pid: nil, id_pid: nil, pk: pk}) end defp run(state) do handle_messages() id_pid = case state.id_pid do nil -> SApp.Identity.find_proc(state.pk) x -> x end state = put_in(state.id_pid, id_pid) nick = case id_pid do nil -> SApp.Identity.default_nick(state.pk) _ -> info = GenServer.call(id_pid, :get_info) info.nick end prompt = case state.room_pid do nil -> "(no channel) #{nick}: " _ -> %SApp.Chat.Manifest{channel: chan} = GenServer.call(state.room_pid, :manifest) "##{chan} #{nick}: " end str = prompt |> IO.gets |> String.trim cond do str == "/quit" -> nil String.slice(str, 0..0) == "/" -> command = str |> String.slice(1..-1) |> String.split(" ") state = handle_command(state, command) run(state) true -> if str != "" do GenServer.cast(state.room_pid, {:chat_send, state.pk, str}) end run(state) end end defp handle_messages() do receive do {:chat_recv, chan, {pk, msgbin, _sign}} -> {ts, msg} = SData.term_unbin msgbin nick = SApp.Identity.get_nick pk IO.puts "#{ts |> DateTime.from_unix! |> DateTime.to_iso8601} ##{chan} <#{nick} #{Shard.Keys.pk_display pk}> #{msg}" handle_messages() {:chat_send, _, _} -> # do nothing handle_messages() after 10 -> nil end end defp handle_command(state, ["connect", ipstr, portstr]) do {:ok, ip} = :inet.parse_address (to_charlist ipstr) {port, _} = Integer.parse portstr Shard.Manager.add_peer(ip, port) state end defp handle_command(state, ["list"]) do IO.puts "List of known channels:" for {_chid, %SApp.Chat.Manifest{channel: chan}, _chpid} <- Shard.Manager.list_shards do IO.puts "##{chan}" end state end defp handle_command(state, ["hist"]) do if state.room_pid == nil do IO.puts "Not currently on a channel!" else GenServer.call(state.room_pid, {:read_history, nil, 25}) |> Enum.each(fn {{pk, msgbin, _sign}, true} -> {ts, msg} = SData.term_unbin msgbin nick = SApp.Identity.get_nick pk IO.puts "#{ts |> DateTime.from_unix! |> DateTime.to_iso8601} <#{nick} #{Shard.Keys.pk_display pk}> #{msg}" end) end state end defp handle_command(state, ["join", qchan]) do pid = SApp.Chat.find_proc qchan case pid do nil -> {:ok, pid} = Shard.Manifest.start %SApp.Chat.Manifest{channel: qchan} GenServer.cast(pid, {:subscribe, self()}) %{state | room_pid: pid} pid -> IO.puts "Switching to ##{qchan}" %{state | room_pid: pid} end end defp handle_command(state, ["nick", nick]) do pid = case state.id_pid do nil -> SApp.Identity.find_proc state.pk x -> x end if pid == nil do IO.puts "Sorry, we have a problem with the identity shard" else info = GenServer.call(pid, :get_info) GenServer.call(pid, {:set_info, %{info | nick: nick}}) end state end defp handle_command(state, _cmd) do IO.puts "Invalid command" state end end