aboutsummaryrefslogtreecommitdiff
path: root/shard/lib/cli/cli.ex
blob: 3778b2d9e83d3d4cf5df5916369c1ce8ed997c06 (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
134
135
136
137
138
139
140
141
142
143
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 = case Shard.Keys.list_identities do
      [pk1|_] -> pk1
      _ ->
        IO.puts "Generating a new identity..."
        Shard.Keys.new_identity
    end

    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, {ts, nick, msg}} ->
        IO.puts "#{ts |> DateTime.from_unix! |> DateTime.to_iso8601}  ##{chan}  <#{nick}> #{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 = case SApp.Identity.find_proc pk do
          nil ->
            SApp.Identity.default_nick pk
          pid ->
            info = GenServer.call(pid, :get_info)
            info.nick
        end
          IO.puts "#{ts |> DateTime.from_unix! |> DateTime.to_iso8601}  <#{nick} #{pk|>binary_part(0, 4)|>Base.encode16|>String.downcase}> #{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