diff options
Diffstat (limited to 'lib/irc/user_track.ex')
-rw-r--r-- | lib/irc/user_track.ex | 177 |
1 files changed, 146 insertions, 31 deletions
diff --git a/lib/irc/user_track.ex b/lib/irc/user_track.ex index 02d5752..08b5160 100644 --- a/lib/irc/user_track.ex +++ b/lib/irc/user_track.ex @@ -4,7 +4,7 @@ defmodule IRC.UserTrack do """ @ets IRC.UserTrack.Storage - # {uuid, nick, nicks, privilege_map} + # {uuid, network, nick, nicks, privilege_map} # Privilege map: # %{"#channel" => [:operator, :voice] defmodule Storage do @@ -49,24 +49,87 @@ defmodule IRC.UserTrack do defmodule Id, do: use EntropyString defmodule User do - defstruct [:id, :nick, :nicks, :username, :host, :realname, :privileges] + defstruct [:id, :account, :network, :nick, {:nicks, []}, :username, :host, :realname, {:privileges, %{}}, {:last_active, %{}}] def to_tuple(u = %__MODULE__{}) do - {u.id || IRC.UserTrack.Id.large_id, u.nick, u.nicks || [], u.username, u.host, u.realname, u.privileges} + {u.id || IRC.UserTrack.Id.large_id, u.network, u.account, String.downcase(u.nick), u.nick, u.nicks || [], u.username, u.host, u.realname, u.privileges, u.last_active} end - def from_tuple({id, nick, nicks, username, host, realname, privs}) do - %__MODULE__{id: id, nick: nick, nicks: nicks, username: username, realname: realname, privileges: privs} + #tuple size: 11 + def from_tuple({id, network, account, _downcased_nick, nick, nicks, username, host, realname, privs, last_active}) do + struct = %__MODULE__{id: id, account: account, network: network, nick: nick, nicks: nicks, username: username, host: host, realname: realname, privileges: privs, last_active: last_active} end end - def find_by_nick(nick) do - case :ets.match(@ets, {:'$1', nick, :_, :_, :_, :_, :_}) do - [[id]] -> lookup(id) + def find_by_account(%IRC.Account{id: id}) do + #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end) + spec = [ + {{:_, :_, :"$2", :_, :_, :_, :_, :_, :_, :_, :_}, + [ + {:==, :"$2", {:const, id}} + ], [:"$_"]} + ] + for obj <- :ets.select(@ets, spec), do: User.from_tuple(obj) + end + + def find_by_account(network, nil) do + nil + end + + def find_by_account(network, %IRC.Account{id: id}) do + #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end) + spec = [ + {{:_, :"$1", :"$2", :_, :_, :_, :_, :_, :_, :_, :_}, + [ + {:andalso, {:==, :"$1", {:const, network}}, + {:==, :"$2", {:const, id}}} + ], [:"$_"]} + ] + case :ets.select(@ets, spec) do + results = [_r | _] -> + results + |> Enum.sort_by(fn({_, _, _, _, _, _, _, _, _, _, actives}) -> + Map.get(actives, nil) + end, {:desc, NaiveDateTime}) + |> List.first + |> User.from_tuple() _ -> nil end end + + def merge_account(old_id, new_id) do + #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end) + spec = [ + {{:_, :_, :"$1", :_, :_, :_, :_, :_, :_, :_, :_}, + [ + {:==, :"$1", {:const, old_id}} + ], [:"$_"]} + ] + Enum.each(:ets.select(@ets, spec), fn({id, net, _, downcased_nick, nick, nicks, username, host, realname, privs, active}) -> + Storage.op(fn(ets) -> + :ets.insert(@ets, {id, net, new_id, downcased_nick, nick, nicks, username, host, realname, privs, active}) + end) + end) + end + + def find_by_nick(%ExIRC.Who{network: network, nick: nick}) do + find_by_nick(network, nick) + end + + + def find_by_nick(%ExIRC.SenderInfo{network: network, nick: nick}) do + find_by_nick(network, nick) + end + + def find_by_nick(network, nick) do + case :ets.match(@ets, {:"$1", network, :_, String.downcase(nick), :_, :_, :_, :_, :_, :_, :_}) do + [[id]] -> lookup(id) + _ -> + nil + end + end + def to_list, do: :ets.tab2list(@ets) def lookup(id) do @@ -76,8 +139,8 @@ defmodule IRC.UserTrack do end end - def operator?(channel, nick) do - if user = find_by_nick(nick) do + def operator?(network, channel, nick) do + if user = find_by_nick(network, nick) do privs = Map.get(user.privileges, channel, []) Enum.member?(privs, :admin) || Enum.member?(privs, :operator) else @@ -85,22 +148,34 @@ defmodule IRC.UserTrack do end end - def channel(channel) do - Enum.filter(to_list(), fn({_, nick, _, _, _, _, channels}) -> + def channel(network, channel) do + Enum.filter(to_list(), fn({_, network, _, _, _, _, _, _, _, channels, _}) -> Map.get(channels, channel) end) end + # TODO + def connected(sender = %{nick: nick}) do + end + def joined(c, s), do: joined(c,s,[]) - def joined(channel, sender=%{nick: nick, user: uname, host: host}, privileges) do + def joined(channel, sender=%{nick: nick, user: uname, host: host}, privileges, touch \\ true) do privileges = if IRC.admin?(sender) do privileges ++ [:admin] else privileges end - user = if user = find_by_nick(nick) do + user = if user = find_by_nick(sender.network, nick) do %User{user | username: uname, host: host, privileges: Map.put(user.privileges || %{}, channel, privileges)} else - %User{nick: nick, username: uname, host: host, privileges: %{channel => privileges}} + user = %User{network: sender.network, nick: nick, username: uname, host: host, privileges: %{channel => privileges}} + + account = IRC.Account.lookup(user).id + user = %User{user | account: account} + end + user = touch_struct(user, channel) + + if touch && user.account do + IRC.Membership.touch(user.account, sender.network, channel) end Storage.op(fn(ets) -> @@ -108,27 +183,47 @@ defmodule IRC.UserTrack do end) end - def joined(channel, nick, privileges) do - user = if user = find_by_nick(nick) do - %User{user | privileges: Map.put(user.privileges, channel, privileges)} + #def joined(network, channel, nick, privileges) do + # user = if user = find_by_nick(network, nick) do + # %User{user | privileges: Map.put(user.privileges, channel, privileges)} + # else + # %User{nick: nick, privileges: %{channel => privileges}} + # end + # + # Storage.op(fn(ets) -> + # :ets.insert(ets, User.to_tuple(user)) + # end) + #end + + def messaged(%IRC.Message{network: network, account: account, channel: chan, sender: %{nick: nick}} = m) do + {user, account} = if user = find_by_nick(network, nick) do + {touch_struct(user, chan), account || IRC.Account.lookup(user)} else - %User{nick: nick, privileges: %{channel => privileges}} + user = %User{network: network, nick: nick, privileges: %{}} + account = IRC.Account.lookup(user) + {%User{user | account: account.id}, account} + end + Storage.insert(User.to_tuple(user)) + if chan, do: IRC.Membership.touch(account, network, chan) + if !m.account do + {:ok, %IRC.Message{m | account: account}} + else + :ok end - - Storage.op(fn(ets) -> - :ets.insert(ets, User.to_tuple(user)) - end) end - def renamed(old_nick, new_nick) do - if user = find_by_nick(old_nick) do + def renamed(network, old_nick, new_nick) do + if user = find_by_nick(network, old_nick) do + old_account = IRC.Account.lookup(user) user = %User{user | nick: new_nick, nicks: [old_nick|user.nicks]} + account = IRC.Account.lookup(user, false) || old_account + user = %User{user | nick: new_nick, account: account.id, nicks: [old_nick|user.nicks]} Storage.insert(User.to_tuple(user)) end end - def change_privileges(channel, nick, {add, remove}) do - if user = find_by_nick(nick) do + def change_privileges(network, channel, nick, {add, remove}) do + if user = find_by_nick(network, nick) do privs = Map.get(user.privileges, channel) privs = Enum.reduce(add, privs, fn(priv, acc) -> [priv|acc] end) @@ -139,9 +234,18 @@ defmodule IRC.UserTrack do end end - def parted(channel, nick) do - if user = find_by_nick(nick) do + def parted(channel, %{network: network, nick: nick}) do + parted(network, channel, nick) + end + + def parted(network, channel, nick) do + if user = find_by_nick(network, nick) do + if user.account do + IRC.Membership.touch(user.account, network, channel) + end + privs = Map.delete(user.privileges, channel) + lasts = Map.delete(user.last_active, channel) if Enum.count(privs) > 0 do user = %User{user | privileges: privs} Storage.insert(User.to_tuple(user)) @@ -152,9 +256,20 @@ defmodule IRC.UserTrack do end def quitted(sender) do - if user = find_by_nick(sender.nick) do - Storage.delete(user.id) + if user = find_by_nick(sender.network, sender.nick) do + if user.account do + for({channel, _} <- user.privileges, do: IRC.Membership.touch(user.account, sender.network, channel)) + end + Storage.delete(user.id) end end + defp touch_struct(user = %User{last_active: last_active}, channel) do + now = NaiveDateTime.utc_now() + last_active = last_active + |> Map.put(channel, now) + |> Map.put(nil, now) + %User{user | last_active: last_active} + end + end |