summaryrefslogtreecommitdiff
path: root/lib/plugins/tell.ex
blob: 8978c82707223507dbbff1d554b900ffdbb1c843 (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
defmodule Nola.Plugins.Tell do
  use GenServer

  @moduledoc """
  # Tell

  * **!tell `<nick>` `<message>`**: tell `message` to `nick` when they reconnect.
  """

  def irc_doc, do: @moduledoc
  def start_link() do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def dets do
    (Nola.data_path() <> "/tell.dets") |> String.to_charlist()
  end

  def tell(m, target, message) do
    GenServer.cast(__MODULE__, {:tell, m, target, message})
  end

  def init([]) do
    regopts = [plugin: __MODULE__]
    {:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
    {:ok, _} = Registry.register(IRC.PubSub, "trigger:tell", regopts)
    {:ok, dets} = :dets.open_file(dets(), [type: :bag])
    {:ok, %{dets: dets}}
  end

  def handle_cast({:tell, m, target, message}, state) do
    do_tell(state, m, target, message)
    {:noreply, state}
  end

  def handle_info({:irc, :trigger, "tell", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang, args: [target | message]}}}, state) do
    do_tell(state, m, target, message)
    {:noreply, state}
  end

  def handle_info({:account, network, channel, nick, account_id}, state) do
    messages = :dets.lookup(state.dets, {network, channel, account_id})
    if messages != [] do
      strs = Enum.map(messages, fn({_, from, message, at}) ->
        account = IRC.Account.get(from)
        user = IRC.UserTrack.find_by_account(network, account)
        fromnick = if user, do: user.nick, else: account.name
        "#{nick}: <#{fromnick}> #{message}"
      end)
      Enum.each(strs, fn(s) -> IRC.Connection.broadcast_message(network, channel, s) end)
      :dets.delete(state.dets, {network, channel, account_id})
    end
    {:noreply, state}
  end

  def handle_info({:account_change, old_id, new_id}, state) do
    #:ets.fun2ms(fn({ {_net, _chan, target_id}, from_id, _, _} = obj) when (target_id == old_id) or (from_id == old_id) -> obj end)
    spec = [{{{:"$1", :"$2", :"$3"}, :"$4", :_, :_}, [{:orelse, {:==, :"$3", {:const, old_id}}, {:==, :"$4", {:const, old_id}}}], [:"$_"]}]
    Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
      case obj do
        { {net, chan, ^old_id}, from_id, message, at } = obj ->
          :dets.delete(obj)
          :dets.insert(table, {{net, chan, new_id}, from_id, message, at})
        {key, ^old_id, message, at} = obj ->
          :dets.delete(table, obj)
          :dets.insert(table, {key, new_id, message, at})
        _ -> :ok
      end
    end)
    {:noreply, state}
  end


  def handle_info(info, state) do
    {:noreply, state}
  end

  def terminate(_, state) do
    :dets.close(state.dets)
    :ok
  end

  defp do_tell(state, m, nick_target, message) do
    target = IRC.Account.find_always_by_nick(m.network, m.channel, nick_target)
    message = Enum.join(message, " ")
    with \
         {:target, %IRC.Account{} = target} <- {:target, target},
         {:same, false} <- {:same, target.id == m.account.id},
        target_user = IRC.UserTrack.find_by_account(m.network, target),
        target_nick = if(target_user, do: target_user.nick, else: target.name),
        present? = if(target_user, do: Map.has_key?(target_user.last_active, m.channel)),
         {:absent, true, _} <- {:absent, !present?, target_nick},
         {:message, message} <- {:message, message}
    do
      obj = { {m.network, m.channel, target.id}, m.account.id, message, NaiveDateTime.utc_now()}
      :dets.insert(state.dets, obj)
      m.replyfun.("will tell to #{target_nick}")
    else
      {:same, _} -> m.replyfun.("are you so stupid that you need a bot to tell yourself things ?")
      {:target, _} -> m.replyfun.("#{nick_target} unknown")
      {:absent, _, nick} -> m.replyfun.("#{nick} is here, tell yourself!")
      {:message, _} -> m.replyfun.("can't tell without a message")
    end
  end

end