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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
defmodule LSG.IRC.UserTrack do
@moduledoc """
User Track DB & Utilities
"""
@ets LSG.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
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 || LSG.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 LSG.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
|