summaryrefslogtreecommitdiff
path: root/lib/irc/user_track.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/irc/user_track.ex')
-rw-r--r--lib/irc/user_track.ex177
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