summaryrefslogtreecommitdiff
path: root/lib/irc/pubsub_handler.ex
blob: 3e78d7b8e09631169f2908a847fcb034209174fa (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
defmodule IRC.PubSubHandler do
  @moduledoc """
  # IRC PubSub

  Provides a nicer abstraction over ExIRC's handlers.

  ## PubSub topics

  * `message` -- all messages (including triggers)
  * `message:private` -- all messages without a channel
  * `message:#CHANNEL` -- all messages within `#CHANNEL`
  * `triggers` -- all triggers
  * `trigger:TRIGGER` -- any message with a trigger `TRIGGER`

  ## Replying to %IRC.Message{}

  Each `IRC.Message` comes with a dedicated `replyfun`, to which you only have to pass either:

  """
  def irc_doc, do: nil

  def start_link(client) do
    GenServer.start_link(__MODULE__, [client], [name: __MODULE__])
  end

  def init([client]) do
    ExIRC.Client.add_handler(client, self())
    {:ok, client}
  end

  @triggers %{
    "!" => :bang,
    "+" => :plus,
    "-" => :minus,
    "?" => :query,
    "." => :dot,
  }

  def handle_info({:received, text, sender, chan}, client) do
    reply_fun = fn(text) -> irc_reply(client, {chan, sender}, text) end
    message = %IRC.Message{text: text, sender: sender, channel: chan, replyfun: reply_fun, trigger: extract_trigger(text)}
    publish(message, ["message:#{chan}"])
    {:noreply, client}
  end

  def handle_info({:received, text, sender}, client) do
    reply_fun = fn(text) -> irc_reply(client, {sender.nick, sender}, text) end
    message = %IRC.Message{text: text, sender: sender, replyfun: reply_fun, trigger: extract_trigger(text)}
    publish(message, ["message:private"])
    {:noreply, client}
  end

  def handle_info(unhandled, client) do
    IO.puts inspect(unhandled)
    {:noreply, client}
  end

  defp publish(pub), do: publish(pub, [])

  defp publish(m = %IRC.Message{trigger: nil}, keys) do
    dispatch(["message"] ++ keys, {:irc, :text, m})
  end

  defp publish(m = %IRC.Message{trigger: t = %IRC.Trigger{trigger: trigger}}, keys) do
    dispatch(["message", "triggers", "trigger:"<>trigger]++keys, {:irc, :trigger, trigger, m})
  end

  defp dispatch(key, content) when is_binary(key), do: dispatch([key], content)
  defp dispatch(keys, content) when is_list(keys) do
    IO.puts "dispatching to #{inspect(keys)} --> #{inspect content}"
    for key <- keys do
      spawn(fn() -> Registry.dispatch(IRC.PubSub, key, fn h ->
        for {pid, _} <- h, do: send(pid, content)
      end) end)
    end
  end

  #
  # Triggers
  #


  for {trigger, name} <- @triggers do
    defp extract_trigger(unquote(trigger)<>text) do
      text = String.strip(text)
      [trigger | args] = String.split(text, " ")
      %IRC.Trigger{type: unquote(name), trigger: trigger, args: args}
    end
  end

  defp extract_trigger(_), do: nil

  #
  # IRC Replies
  #

  # irc_reply(ExIRC.Client pid, {channel or nick, ExIRC.Sender}, binary | replies
  # replies :: {:kick, reason} | {:kick, nick, reason} | {:mode, mode, nick}
  defp irc_reply(client, {target, _}, text) when is_binary(text) do
    ExIRC.Client.msg(client, :privmsg, target, text)
  end

  defp irc_reply(client, {target, %{nick: nick}}, {:kick, reason}) do
    ExIRC.Client.kick(client, target, nick, reason)
  end

  defp irc_reply(client, {target, _}, {:kick, nick, reason}) do
    ExIRC.Client.kick(client, target, nick, reason)
  end

  defp irc_reply(client, {target, %{nick: nick}}, {:mode, mode}) do
    ExIRC.Client.mode(client, target, mode, nick)
  end

  defp irc_reply(client, target, {:mode, mode, nick}) do
    ExIRC.Client.mode(client, target, mode, nick)
  end

  defp irc_reply(client, target, {:channel_mode, mode}) do
    ExIRC.Client.mode(client, target, mode)
  end

end