summaryrefslogtreecommitdiff
path: root/lib/nola/membership.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nola/membership.ex')
-rw-r--r--lib/nola/membership.ex129
1 files changed, 129 insertions, 0 deletions
diff --git a/lib/nola/membership.ex b/lib/nola/membership.ex
new file mode 100644
index 0000000..b98efd7
--- /dev/null
+++ b/lib/nola/membership.ex
@@ -0,0 +1,129 @@
+defmodule Nola.Membership do
+ @moduledoc """
+ Memberships (users in channels)
+ """
+
+ # Key: {account, net, channel}
+ # Format: {key, last_seen}
+
+ defp dets() do
+ to_charlist(Nola.data_path <> "/memberships.dets")
+ end
+
+ def start_link() do
+ GenServer.start_link(__MODULE__, [], [name: __MODULE__])
+ end
+
+ def init(_) do
+ dets = :dets.open_file(dets(), [])
+ {:ok, dets}
+ end
+
+ def of_account(%Nola.Account{id: id}) do
+ spec = [{{{:"$1", :"$2", :"$3"}, :_}, [{:==, :"$1", {:const, id}}], [{{:"$2", :"$3"}}]}]
+ :dets.select(dets(), spec)
+ end
+
+ def merge_account(old_id, new_id) do
+ #iex(37)> :ets.fun2ms(fn({{old_id, _, _}, _}=obj) when old_id == "42" -> obj end)
+ spec = [{{{:"$1", :_, :_}, :_}, [{:==, :"$1", {:const, old_id}}], [:"$_"]}]
+ Util.ets_mutate_select_each(:dets, dets(), spec, fn(table, obj = {{_old, net, chan}, ts}) ->
+ :dets.delete_object(table, obj)
+ :dets.insert(table, {{new_id, net, chan}, ts})
+ end)
+ end
+
+ def touch(%Nola.Account{id: id}, network, channel) do
+ :dets.insert(dets(), {{id, network, channel}, NaiveDateTime.utc_now()})
+ end
+ def touch(account_id, network, channel) do
+ if account = Nola.Account.get(account_id) do
+ touch(account, network, channel)
+ end
+ end
+
+ def notify_channels(account, minutes \\ 30, last_active \\ true) do
+ not_before = NaiveDateTime.add(NaiveDateTime.utc_now(), (minutes*-60), :second)
+ spec = [{{{:"$1", :_, :_}, :_}, [{:==, :"$1", {:const, account.id}}], [:"$_"]}]
+ memberships = :dets.select(dets(), spec)
+ |> Enum.sort_by(fn({_, ts}) -> ts end, {:desc, NaiveDateTime})
+ active_memberships = Enum.filter(memberships, fn({_, ts}) -> NaiveDateTime.compare(ts, not_before) == :gt end)
+ cond do
+ active_memberships == [] && last_active ->
+ case memberships do
+ [{{_, net, chan}, _}|_] -> [{net, chan}]
+ _ -> []
+ end
+ active_memberships == [] ->
+ []
+ true ->
+ Enum.map(active_memberships, fn({{_, net, chan}, _}) -> {net,chan} end)
+ end
+ end
+
+ def members_or_friends(account, _network, nil) do
+ friends(account)
+ end
+
+ def members_or_friends(_, network, channel) do
+ members(network, channel)
+ end
+
+ def expanded_members_or_friends(account, network, channel) do
+ expand(network, members_or_friends(account, network, channel))
+ end
+
+ def expanded_members(network, channel) do
+ expand(network, members(network, channel))
+ end
+
+ def members(network, channel) do
+ #iex(19)> :ets.fun2ms(fn({{id, net, chan}, ts}) when net == network and chan == channel and ts > min_seen -> id end)
+ limit = 0 # NaiveDateTime.add(NaiveDateTime.utc_now, 30*((24*-60)*60), :second)
+ spec = [
+ {{{:"$1", :"$2", :"$3"}, :"$4"},
+ [
+ {:andalso,
+ {:andalso, {:==, :"$2", {:const, network}}, {:==, :"$3", {:const, channel}}},
+ {:>, :"$4", {:const, limit}}}
+ ], [:"$1"]}
+ ]
+ :dets.select(dets(), spec)
+ end
+
+ def friends(account = %Nola.Account{id: id}) do
+ for({net, chan} <- of_account(account), do: members(net, chan))
+ |> List.flatten()
+ |> Enum.uniq()
+ end
+
+ def handle_info(_, dets) do
+ {:noreply, dets}
+ end
+
+ def handle_cast(_, dets) do
+ {:noreply, dets}
+ end
+
+ def handle_call(_, _, dets) do
+ {:noreply, dets}
+ end
+
+ def terminate(_, dets) do
+ :dets.sync(dets)
+ :dets.close(dets)
+ end
+
+ defp expand(network, list) do
+ for id <- list do
+ if account = Nola.Account.get(id) do
+ user = IRC.UserTrack.find_by_account(network, account)
+ nick = if(user, do: user.nick, else: account.name)
+ {account, user, nick}
+ end
+ end
+ |> Enum.filter(fn(x) -> x end)
+ end
+
+
+end