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.ex154
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