diff options
Diffstat (limited to 'lib/nola/user_track.ex')
-rw-r--r-- | lib/nola/user_track.ex | 323 |
1 files changed, 230 insertions, 93 deletions
diff --git a/lib/nola/user_track.ex b/lib/nola/user_track.ex index c1218b0..0b07a91 100644 --- a/lib/nola/user_track.ex +++ b/lib/nola/user_track.ex @@ -8,23 +8,23 @@ defmodule Nola.UserTrack do # Privilege map: # %{"#channel" => [:operator, :voice] defmodule Storage do - def delete(id) do - op(fn(ets) -> :ets.delete(ets, id) end) + op(fn ets -> :ets.delete(ets, id) end) end def insert(tuple) do - op(fn(ets) -> :ets.insert(ets, tuple) end) + op(fn ets -> :ets.insert(ets, tuple) end) end def clear_network(network) do - op(fn(ets) -> + op(fn ets -> spec = [ {{:_, :"$1", :_, :_, :_, :_, :_, :_, :_, :_, :_, :_}, - [ - {:==, :"$1", {:const, network}} - ], [:"$_"]} + [ + {:==, :"$1", {:const, network}} + ], [:"$_"]} ] + :ets.match_delete(ets, spec) end) end @@ -34,7 +34,7 @@ defmodule Nola.UserTrack do end def start_link do - GenServer.start_link(__MODULE__, [], [name: __MODULE__]) + GenServer.start_link(__MODULE__, [], name: __MODULE__) end def init([]) do @@ -43,13 +43,15 @@ defmodule Nola.UserTrack do end def handle_call({:op, fun}, _from, ets) do - returned = try do - {:ok, fun.(ets)} - rescue - rescued -> {:error, rescued} - catch - rescued -> {:error, rescued} - end + returned = + try do + {:ok, fun.(ets)} + rescue + rescued -> {:error, rescued} + catch + rescued -> {:error, rescued} + end + {:reply, returned, ets} end @@ -58,31 +60,63 @@ defmodule Nola.UserTrack do end end - defmodule Id, do: use EntropyString + defmodule Id, do: use(EntropyString) defmodule User do - defstruct [:id, :account, :network, :nick, {:nicks, []}, :username, :host, :realname, {:privileges, %{}}, {:last_active, %{}}, {:options, %{}}] + defstruct [ + :id, + :account, + :network, + :nick, + {:nicks, []}, + :username, + :host, + :realname, + {:privileges, %{}}, + {:last_active, %{}}, + {:options, %{}} + ] def to_tuple(u = %__MODULE__{}) do - {u.id || Nola.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, u.options} + {u.id || Nola.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, + u.options} end - #tuple size: 11 - def from_tuple({id, network, account, _downcased_nick, nick, nicks, username, host, realname, privs, last_active, opts}) 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, options: opts} + # tuple size: 11 + def from_tuple( + {id, network, account, _downcased_nick, nick, nicks, username, host, realname, privs, + last_active, opts} + ) 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, + options: opts + } end end def find_by_account(%Nola.Account{id: id}) do - #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end) + # iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end) spec = [ {{:_, :_, :"$2", :_, :_, :_, :_, :_, :_, :_, :_, :_}, [ - {:==, :"$2", {:const, id}} + {:==, :"$2", {:const, id}} ], [:"$_"]} ] - results = :ets.select(@ets, spec) - |> Enum.filter(& &1) + + results = + :ets.select(@ets, spec) + |> Enum.filter(& &1) + for obj <- results, do: User.from_tuple(obj) end @@ -91,27 +125,39 @@ defmodule Nola.UserTrack do end def find_by_account(network, %Nola.Account{id: id}) do - #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end) + # 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}}} + {:andalso, {:==, :"$1", {:const, network}}, {:==, :"$2", {:const, id}}} ], [:"$_"]} ] + case :ets.select(@ets, spec) do results = [_r | _] -> - result = results - |> Enum.reject(fn({_, net, _, _, _, _, _, _, _, _, actives, opts}) -> network != "matrix" && net == "matrix" end) - |> Enum.reject(fn({_, net, _, _, _, _, _, _, _, _, actives, opts}) -> network != "telegram" && net == "telegram" end) - |> Enum.reject(fn({_, _, _, _, _, _, _, _, _, _, actives, opts}) -> network not in ["matrix", "telegram"] && Map.get(opts, :puppet) end) - |> Enum.sort_by(fn({_, _, _, _, _, _, _, _, _, _, actives, _}) -> - Map.get(actives, nil) - end, {:desc, NaiveDateTime}) - |> List.first + result = + results + |> Enum.reject(fn {_, net, _, _, _, _, _, _, _, _, actives, opts} -> + network != "matrix" && net == "matrix" + end) + |> Enum.reject(fn {_, net, _, _, _, _, _, _, _, _, actives, opts} -> + network != "telegram" && net == "telegram" + end) + |> Enum.reject(fn {_, _, _, _, _, _, _, _, _, _, actives, opts} -> + network not in ["matrix", "telegram"] && Map.get(opts, :puppet) + end) + |> Enum.sort_by( + fn {_, _, _, _, _, _, _, _, _, _, actives, _} -> + Map.get(actives, nil) + end, + {:desc, NaiveDateTime} + ) + |> List.first() if result, do: User.from_tuple(result) - _ -> nil + + _ -> + nil end end @@ -119,18 +165,23 @@ defmodule Nola.UserTrack do Storage.clear_network(network) 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) + # iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end) spec = [ {{:_, :_, :"$1", :_, :_, :_, :_, :_, :_, :_, :_, :_}, [ - {:==, :"$1", {:const, old_id}} + {:==, :"$1", {:const, old_id}} ], [:"$_"]} ] - Enum.each(:ets.select(@ets, spec), fn({id, net, _, downcased_nick, nick, nicks, username, host, realname, privs, active, opts}) -> - Storage.op(fn(ets) -> - :ets.insert(@ets, {id, net, new_id, downcased_nick, nick, nicks, username, host, realname, privs, active, opts}) + + Enum.each(:ets.select(@ets, spec), fn {id, net, _, downcased_nick, nick, nicks, username, + host, realname, privs, active, opts} -> + Storage.op(fn ets -> + :ets.insert( + @ets, + {id, net, new_id, downcased_nick, nick, nicks, username, host, realname, privs, active, + opts} + ) end) end) end @@ -139,14 +190,18 @@ defmodule Nola.UserTrack 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) + case :ets.match( + @ets, + {:"$1", network, :_, String.downcase(nick), :_, :_, :_, :_, :_, :_, :_, :_} + ) do + [[id] | _] -> + lookup(id) + _ -> nil end @@ -171,7 +226,7 @@ defmodule Nola.UserTrack do end def channel(network, channel) do - Enum.filter(to_list(), fn({_, network, _, _, _, _, _, _, _, channels, _, _}) -> + Enum.filter(to_list(), fn {_, network, _, _, _, _, _, _, _, channels, _, _} -> Map.get(channels, channel) end) end @@ -179,53 +234,92 @@ defmodule Nola.UserTrack do # TODO def connected(network, nick, user, host, account_id, opts \\ %{}) do if account = Nola.Account.get(account_id) do - user = if user = find_by_nick(network, nick) do - user - else - user = %User{id: Nola.UserTrack.Id.large_id, account: account_id, network: network, nick: nick, username: user, host: host, privileges: %{}, options: opts} - Storage.op(fn(ets) -> - :ets.insert(ets, User.to_tuple(user)) - end) - user - end + user = + if user = find_by_nick(network, nick) do + user + else + user = %User{ + id: Nola.UserTrack.Id.large_id(), + account: account_id, + network: network, + nick: nick, + username: user, + host: host, + privileges: %{}, + options: opts + } + + Storage.op(fn ets -> + :ets.insert(ets, User.to_tuple(user)) + end) + + user + end + + Nola.Irc.Connection.publish_event(network, %{ + type: :connect, + user_id: user.id, + account_id: user.account + }) - Nola.Irc.Connection.publish_event(network, %{type: :connect, user_id: user.id, account_id: user.account}) :ok else :error end end - def joined(c, s), do: joined(c,s,[]) + def joined(c, s), do: joined(c, s, []) - def joined(channel, sender=%{nick: nick, user: uname, host: host}, privileges, touch \\ true) do - privileges = if Nola.Irc.admin?(sender) do - privileges ++ [:admin] - else privileges end - 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 = %User{id: Nola.UserTrack.Id.large_id, network: sender.network, nick: nick, username: uname, host: host, privileges: %{channel => privileges}} + def joined(channel, sender = %{nick: nick, user: uname, host: host}, privileges, touch \\ true) do + privileges = + if Nola.Irc.admin?(sender) do + privileges ++ [:admin] + else + privileges + end + + 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 = %User{ + id: Nola.UserTrack.Id.large_id(), + network: sender.network, + nick: nick, + username: uname, + host: host, + privileges: %{channel => privileges} + } + + account = Nola.Account.lookup(user).id + user = %User{user | account: account} + end - account = Nola.Account.lookup(user).id - user = %User{user | account: account} - end user = touch_struct(user, channel) if touch && user.account do Nola.Membership.touch(user.account, sender.network, channel) end - Storage.op(fn(ets) -> + Storage.op(fn ets -> :ets.insert(ets, User.to_tuple(user)) end) - Nola.Irc.Connection.publish_event({sender.network, channel}, %{type: :join, user_id: user.id, account_id: user.account}) + Nola.Irc.Connection.publish_event({sender.network, channel}, %{ + type: :join, + user_id: user.id, + account_id: user.account + }) user end - #def joined(network, channel, nick, privileges) do + # 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 @@ -235,18 +329,24 @@ defmodule Nola.UserTrack do # Storage.op(fn(ets) -> # :ets.insert(ets, User.to_tuple(user)) # end) - #end + # end + + def messaged( + %Nola.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 || Nola.Account.lookup(user)} + else + user = %User{network: network, nick: nick, privileges: %{}} + account = Nola.Account.lookup(user) + {%User{user | account: account.id}, account} + end - def messaged(%Nola.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 || Nola.Account.lookup(user)} - else - user = %User{network: network, nick: nick, privileges: %{}} - account = Nola.Account.lookup(user) - {%User{user | account: account.id}, account} - end Storage.insert(User.to_tuple(user)) if chan, do: Nola.Membership.touch(account, network, chan) + if !m.account do {:ok, %Nola.Message{m | account: account}} else @@ -257,12 +357,19 @@ defmodule Nola.UserTrack do def renamed(network, old_nick, new_nick) do if user = find_by_nick(network, old_nick) do old_account = Nola.Account.lookup(user) - user = %User{user | nick: new_nick, nicks: [old_nick|user.nicks]} + user = %User{user | nick: new_nick, nicks: [old_nick | user.nicks]} account = Nola.Account.lookup(user, false) || old_account - user = %User{user | nick: new_nick, account: account.id, nicks: [old_nick|user.nicks]} + user = %User{user | nick: new_nick, account: account.id, nicks: [old_nick | user.nicks]} Storage.insert(User.to_tuple(user)) channels = for {channel, _} <- user.privileges, do: channel - Nola.Irc.Connection.publish_event(network, %{type: :nick, user_id: user.id, account_id: account.id, nick: new_nick, old_nick: old_nick}) + + Nola.Irc.Connection.publish_event(network, %{ + type: :nick, + user_id: user.id, + account_id: account.id, + nick: new_nick, + old_nick: old_nick + }) end end @@ -270,12 +377,19 @@ defmodule Nola.UserTrack 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) - privs = Enum.reduce(remove, privs, fn(priv, acc) -> List.delete(acc, priv) end) + 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)) - Nola.Irc.Connection.publish_event({network, channel}, %{type: :privileges, user_id: user.id, account_id: user.account, added: add, removed: remove}) + + Nola.Irc.Connection.publish_event({network, channel}, %{ + type: :privileges, + user_id: user.id, + account_id: user.account, + added: add, + removed: remove + }) end end @@ -292,12 +406,25 @@ defmodule Nola.UserTrack do 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)) - Nola.Irc.Connection.publish_event({network, channel}, %{type: :part, user_id: user.id, account_id: user.account, reason: nil}) + + Nola.Irc.Connection.publish_event({network, channel}, %{ + type: :part, + user_id: user.id, + account_id: user.account, + reason: nil + }) else - Nola.Irc.Connection.publish_event(network, %{type: :quit, user_id: user.id, account_id: user.account, reason: "Left all known channels"}) + Nola.Irc.Connection.publish_event(network, %{ + type: :quit, + user_id: user.id, + account_id: user.account, + reason: "Left all known channels" + }) + Storage.delete(user.id) end end @@ -309,17 +436,27 @@ defmodule Nola.UserTrack do for {channel, _} <- user.privileges do Nola.Membership.touch(user.account, sender.network, channel) end - Nola.Irc.Connection.publish_event(sender.network, %{type: :quit, user_id: user.id, account_id: user.account, reason: reason}) + + Nola.Irc.Connection.publish_event(sender.network, %{ + type: :quit, + user_id: user.id, + account_id: user.account, + reason: reason + }) 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) + + last_active = + last_active + |> Map.put(channel, now) + |> Map.put(nil, now) + %User{user | last_active: last_active} end |