summaryrefslogtreecommitdiff
path: root/lib/nola_plugins/preums_plugin.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nola_plugins/preums_plugin.ex')
-rw-r--r--lib/nola_plugins/preums_plugin.ex276
1 files changed, 0 insertions, 276 deletions
diff --git a/lib/nola_plugins/preums_plugin.ex b/lib/nola_plugins/preums_plugin.ex
deleted file mode 100644
index f250e85..0000000
--- a/lib/nola_plugins/preums_plugin.ex
+++ /dev/null
@@ -1,276 +0,0 @@
-defmodule Nola.IRC.PreumsPlugin do
- @moduledoc """
- # preums !!!
-
- * `!preums`: affiche le preums du jour
- * `.preums`: stats des preums
- """
-
- # WIP Scores
- # L'idée c'est de donner un score pour mettre un peu de challenge en pénalisant les preums faciles.
- #
- # Un preums ne vaut pas 1 point, mais plutôt 0.10 ou 0.05, et on arrondi au plus proche. C'est un jeu sur le long
- # terme. Un gros bonus pourrait apporter beaucoup de points.
- #
- # Il faudrait ces données:
- # - moyenne des preums
- # - activité récente du channel et par nb actifs d'utilisateurs
- # (aggréger memberships+usertrack last_active ?)
- # (faire des stats d'activité habituelle (un peu a la pisg) ?)
- # - preums consécutifs
- #
- # Malus:
- # - est proche de la moyenne en faible activité
- # - trop consécutif de l'utilisateur sauf si activité
- #
- # Bonus:
- # - plus le preums est éloigné de la moyenne
- # - après 18h double
- # - plus l'activité est élévée, exponentiel selon la moyenne
- # - derns entre 4 et 6 (pourrait être adapté selon les stats d'activité)
- #
- # WIP Badges:
- # - derns
- # - streaks
- # - faciles
- # - ?
-
- require Logger
-
- @perfects [~r/preum(s|)/i]
-
- # dets {{chan, day = {yyyy, mm, dd}}, nick, now, perfect?, text}
-
- def all(dets) do
- :dets.foldl(fn(i, acc) -> [i|acc] end, [], dets)
- end
-
- def all(dets, channel) do
- fun = fn({{chan, date}, account_id, time, perfect, text}, acc) ->
- if channel == chan do
- [%{date: date, account_id: account_id, time: time, perfect: perfect, text: text} | acc]
- else
- acc
- end
- end
- :dets.foldl(fun, [], dets)
- end
-
- def topnicks(dets, channel, options \\ []) do
- sort_elem = case Keyword.get(options, :sort_by, :score) do
- :score -> 1
- :count -> 0
- end
-
- fun = fn(x = {{chan, date}, account_id, time, perfect, text}, acc) ->
- if (channel == nil and chan) or (channel == chan) do
- {count, points} = Map.get(acc, account_id, {0, 0})
- score = score(chan, account_id, time, perfect, text)
- Map.put(acc, account_id, {count + 1, points + score})
- else
- acc
- end
- end
- :dets.foldl(fun, %{}, dets)
- |> Enum.sort_by(fn({_account_id, value}) -> elem(value, sort_elem) end, &>=/2)
- end
-
- def irc_doc, do: @moduledoc
- def start_link() do
- GenServer.start_link(__MODULE__, [], name: __MODULE__)
- end
-
- def dets do
- (Nola.data_path() <> "/preums.dets") |> String.to_charlist()
- end
-
- def init([]) do
- regopts = [plugin: __MODULE__]
- {:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
- {:ok, _} = Registry.register(IRC.PubSub, "messages", regopts)
- {:ok, _} = Registry.register(IRC.PubSub, "triggers", regopts)
- {:ok, dets} = :dets.open_file(dets(), [{:repair, :force}])
- Util.ets_mutate_select_each(:dets, dets, [{:"$1", [], [:"$1"]}], fn(table, obj) ->
- {key, nick, now, perfect, text} = obj
- case key do
- {{net, {bork,chan}}, date} ->
- :dets.delete(table, key)
- nick = if IRC.Account.get(nick) do
- nick
- else
- if acct = IRC.Account.find_always_by_nick(net, nil, nick) do
- acct.id
- else
- nick
- end
- end
- :dets.insert(table, { { {net,chan}, date }, nick, now, perfect, text})
- {{_net, nil}, _} ->
- :dets.delete(table, key)
- {{net, chan}, date} ->
- if !IRC.Account.get(nick) do
- if acct = IRC.Account.find_always_by_nick(net, chan, nick) do
- :dets.delete(table, key)
- :dets.insert(table, { { {net,chan}, date }, acct.id, now, perfect, text})
- end
- end
- _ ->
- Logger.debug("DID NOT FIX: #{inspect key}")
- end
- end)
- {:ok, %{dets: dets}}
- end
-
- # Latest
- def handle_info({:irc, :trigger, "preums", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang}}}, state) do
- channelkey = {m.network, m.channel}
- state = handle_preums(m, state)
- tz = timezone(channelkey)
- {:ok, now} = DateTime.now(tz, Tzdata.TimeZoneDatabase)
- date = {now.year, now.month, now.day}
- key = {channelkey, date}
- chan_cache = Map.get(state, channelkey, %{})
- item = if i = Map.get(chan_cache, date) do
- i
- else
- case :dets.lookup(state.dets, key) do
- [item = {^key, _account_id, _now, _perfect, _text}] -> item
- _ -> nil
- end
- end
-
- if item do
- {_, account_id, date, _perfect, text} = item
- h = "#{date.hour}:#{date.minute}:#{date.second}"
- account = IRC.Account.get(account_id)
- user = IRC.UserTrack.find_by_account(m.network, account)
- nick = if(user, do: user.nick, else: account.name)
- m.replyfun.("preums: #{nick} à #{h}: “#{text}”")
- end
- {:noreply, state}
- end
-
- # Stats
- def handle_info({:irc, :trigger, "preums", m = %IRC.Message{trigger: %IRC.Trigger{type: :dot}}}, state) do
- channel = {m.network, m.channel}
- state = handle_preums(m, state)
- top = topnicks(state.dets, channel, sort_by: :score)
- |> Enum.map(fn({account_id, {count, score}}) ->
- account = IRC.Account.get(account_id)
- user = IRC.UserTrack.find_by_account(m.network, account)
- nick = if(user, do: user.nick, else: account.name)
- "#{nick}: #{score} (#{count})"
- end)
- |> Enum.intersperse(", ")
- |> Enum.join("")
- msg = unless top == "" do
- "top preums: #{top}"
- else
- "vous êtes tous nuls"
- end
- m.replyfun.(msg)
- {:noreply, state}
- end
-
- # Help
- def handle_info({:irc, :trigger, "preums", m = %IRC.Message{trigger: %IRC.Trigger{type: :query}}}, state) do
- state = handle_preums(m, state)
- msg = "!preums - preums du jour, .preums top preumseurs"
- m.replymsg.(msg)
- {:noreply, state}
- end
-
- # Trigger fallback
- def handle_info({:irc, :trigger, _, m = %IRC.Message{}}, state) do
- state = handle_preums(m, state)
- {:noreply, state}
- end
-
- # Message fallback
- def handle_info({:irc, :text, m = %IRC.Message{}}, state) do
- {:noreply, handle_preums(m, state)}
- end
-
- # Account
- def handle_info({:account_change, old_id, new_id}, state) do
- spec = [{{:_, :"$1", :_, :_, :_}, [{:==, :"$1", {:const, old_id}}], [:"$_"]}]
- Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
- rename_object_owner(table, obj, new_id)
- end)
- {:noreply, state}
- end
-
- # Account: move from nick to account id
- # FIXME: Doesn't seem to work.
- def handle_info({:accounts, accounts}, state) do
- for x={:account, _net, _chan, _nick, _account_id} <- accounts do
- handle_info(x, state)
- end
- {:noreply, state}
- end
- def handle_info({:account, _net, _chan, nick, account_id}, state) do
- nick = String.downcase(nick)
- spec = [{{:_, :"$1", :_, :_, :_}, [{:==, :"$1", {:const, nick}}], [:"$_"]}]
- Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
- Logger.debug("account:: merging #{nick} -> #{account_id}")
- rename_object_owner(table, obj, account_id)
- end)
- {:noreply, state}
- end
-
- def handle_info(_, dets) do
- {:noreply, dets}
- end
-
-
- defp rename_object_owner(table, object = {key, _, now, perfect, time}, new_id) do
- :dets.delete_object(table, key)
- :dets.insert(table, {key, new_id, now, perfect, time})
- end
-
- defp timezone(channel) do
- env = Application.get_env(:nola, Nola.IRC.PreumsPlugin, [])
- channels = Keyword.get(env, :channels, %{})
- channel_settings = Map.get(channels, channel, [])
- default = Keyword.get(env, :default_tz, "Europe/Paris")
- Keyword.get(channel_settings, :tz, default) || default
- end
-
- defp handle_preums(%IRC.Message{channel: nil}, state) do
- state
- end
-
- defp handle_preums(m = %IRC.Message{text: text, sender: sender}, state) do
- channel = {m.network, m.channel}
- tz = timezone(channel)
- {:ok, now} = DateTime.now(tz, Tzdata.TimeZoneDatabase)
- date = {now.year, now.month, now.day}
- key = {channel, date}
- chan_cache = Map.get(state, channel, %{})
- unless i = Map.get(chan_cache, date) do
- case :dets.lookup(state.dets, key) do
- [item = {^key, _nick, _now, _perfect, _text}] ->
- # Preums lost, but wasn't cached
- Map.put(state, channel, %{date => item})
- [] ->
- # Preums won!
- perfect? = Enum.any?(@perfects, fn(perfect) -> Regex.match?(perfect, text) end)
- item = {key, m.account.id, now, perfect?, text}
- :dets.insert(state.dets, item)
- :dets.sync(state.dets)
- Map.put(state, channel, %{date => item})
- {:error, _} = error ->
- Logger.error("#{__MODULE__} dets lookup failed: #{inspect error}")
- state
- end
- else
- state
- end
- end
-
- def score(_chan, _account, _time, _perfect, _text) do
- 1
- end
-
-
-end