diff options
Diffstat (limited to 'lib/irc/user_track.ex')
-rw-r--r-- | lib/irc/user_track.ex | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/lib/irc/user_track.ex b/lib/irc/user_track.ex new file mode 100644 index 0000000..2614d98 --- /dev/null +++ b/lib/irc/user_track.ex @@ -0,0 +1,154 @@ +defmodule IRC.UserTrack do + @moduledoc """ + User Track DB & Utilities + """ + + @ets IRC.UserTrack.Storage + # {uuid, nick, nicks, privilege_map} + # Privilege map: + # %{"#channel" => [:operator, :voice] + defmodule Storage do + + def delete(id) do + op(fn(ets) -> :ets.delete(ets, id) end) + end + + def insert(tuple) do + op(fn(ets) -> :ets.insert(ets, tuple) end) + end + + def op(fun) do + GenServer.call(__MODULE__, {:op, fun}) + end + + def start_link do + GenServer.start_link(__MODULE__, [], [name: __MODULE__]) + end + + def init([]) do + ets = :ets.new(__MODULE__, [:set, :named_table, :protected, {:read_concurrency, true}]) + {:ok, ets} + end + + def handle_call({:op, fun}, _from, ets) do + returned = try do + {:ok, fun.(ets)} + rescue + rescued -> {:error, rescued} + catch + rescued -> {:error, rescued} + end + {:reply, returned, ets} + end + + def terminate(_reason, ets) do + :ok + end + end + + defmodule Id, do: use EntropyString + + defmodule User do + defstruct [:id, :nick, :nicks, :username, :host, :realname, :privileges] + + 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} + 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} + end + end + + def find_by_nick(nick) do + case :ets.match(@ets, {:'$1', nick, :_, :_, :_, :_, :_}) do + [[id]] -> lookup(id) + _ -> nil + end + end + + def to_list, do: :ets.tab2list(@ets) + + def lookup(id) do + case :ets.lookup(@ets, id) do + [] -> nil + [tuple] -> User.from_tuple(tuple) + end + end + + def operator?(channel, nick) do + if user = find_by_nick(nick) do + privs = Map.get(user.privileges, channel, []) + Enum.member?(privs, :admin) || Enum.member?(privs, :operator) + else + false + end + end + + def joined(c, s), do: joined(c,s,[]) + + def joined(channel, sender=%{nick: nick, user: uname, host: host}, privileges) do + privileges = if IRC.admin?(sender) do + privileges ++ [:admin] + else privileges end + user = if user = find_by_nick(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}} + end + + Storage.op(fn(ets) -> + :ets.insert(ets, User.to_tuple(user)) + 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)} + else + %User{nick: nick, privileges: %{channel => privileges}} + 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 + user = %User{user | nick: new_nick, 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 + privs = Map.get(user.privileges, channel) + + privs = Enum.reduce(add, privs, fn(priv, acc) -> [priv|acc] end) + privs = Enum.reduce(remove, privs, fn(priv, acc) -> List.delete(acc, priv) end) + + user = %User{user | privileges: Map.put(user.privileges, channel, privs)} + Storage.insert(User.to_tuple(user)) + end + end + + def parted(channel, nick) do + if user = find_by_nick(nick) do + privs = Map.delete(user.privileges, channel) + if Enum.count(privs) > 0 do + user = %User{user | privileges: privs} + Storage.insert(User.to_tuple(user)) + else + Storage.delete(user.id) + end + end + end + + def quitted(sender) do + if user = find_by_nick(sender.nick) do + Storage.delete(user.id) + end + end + +end |