aboutsummaryrefslogtreecommitdiff
path: root/shard/lib/cli/cli.ex
blob: 2e35e45d79daac9fd4d5fda3f341dd07c6abc64c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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