diff options
author | href <href@random.sh> | 2020-07-07 21:39:10 +0200 |
---|---|---|
committer | href <href@random.sh> | 2020-07-07 21:39:51 +0200 |
commit | d6ee134a5957e299c3ad59011df320b3c41e6e61 (patch) | |
tree | 29567e6635466f8a3415a935b3cc8a777019f5bc /lib/lsg_irc/alcolog_plugin.ex | |
parent | bleh (diff) |
pouet
Diffstat (limited to 'lib/lsg_irc/alcolog_plugin.ex')
-rw-r--r-- | lib/lsg_irc/alcolog_plugin.ex | 872 |
1 files changed, 582 insertions, 290 deletions
diff --git a/lib/lsg_irc/alcolog_plugin.ex b/lib/lsg_irc/alcolog_plugin.ex index f27dcd8..600dc1a 100644 --- a/lib/lsg_irc/alcolog_plugin.ex +++ b/lib/lsg_irc/alcolog_plugin.ex @@ -2,91 +2,29 @@ defmodule LSG.IRC.AlcoologPlugin do require Logger @moduledoc """ - # alcoolisme + # [alcoolog]({{context_path}}/alcoolog) - * **`!santai <montant> <degrés d'alcool> [annotation]`**: enregistre un nouveau verre de `montant` d'une boisson à `degrés d'alcool`. + * **!santai `<cl | (calc)>` `<degrés d'alcool> [annotation]`**: enregistre un nouveau verre de `montant` d'une boisson à `degrés d'alcool`. + * **!santai `<cl | (calc)>` `<beer name>`**: enregistre un nouveau verre de `cl` de la bière `beer name`, et checkin sur Untappd.com. * **-santai**: annule la dernière entrée d'alcoolisme. * **.alcoolisme**: état du channel en temps réel. + * **.alcoolisme `<semaine | Xj>`**: points par jour, sur X j. * **!alcoolisme `[pseudo]`**: affiche les points d'alcoolisme. - * **!alcoolisme `semaine`**: affiche le top des 7 derniers jours + * **!alcoolisme `[pseudo]` `<semaine | Xj>`**: affiche les points d'alcoolisme par jour sur X j. * **+alcoolisme `<h|f>` `<poids en kg>` `[facteur de perte en mg/l (10, 15, 20, 25)]`**: Configure votre profil d'alcoolisme. * **.sobre**: affiche quand la sobriété frappera sur le chan. * **!sobre `[pseudo]`**: affiche quand la sobriété frappera pour `[pseudo]`. * **!sobrepour `<date>`**: affiche tu pourras être sobre pour `<date>`, et si oui, combien de volumes d'alcool peuvent encore être consommés. - * **!alcoolog**: lien pour voir l'état/statistiques et historique de l'alcoolémie du channel. + * **!alcoolog**: ([voir]({{context_path}}/alcoolog)) lien pour voir l'état/statistiques et historique de l'alcoolémie du channel. * **!alcool `<cl>` `<degrés>`**: donne le nombre d'unités d'alcool dans `<cl>` à `<degrés>°`. * **!soif**: c'est quand l'apéro ? - 1 point = 1 volume d'alcool - - Anciennes commandes: - - * **!`<trigger>` `[coeff]` `[annotation]`**: enregistre de l'alcoolisme. - - Triggers/Coeffs: - - * bière: (coeffs: 25, 50/+, 75/++, 100/+++, ++++) - * pinard: (coeffs: +, ++, +++, ++++) - * shot/fort/whisky/rhum/..: (coeffs: +, ++, +++, ++++) - * eau (-1) + 1 point = 1 volume d'alcool. Annotation: champ libre! """ - @triggers %{ - "apero" => - %{ - triggers: ["apero", "apéro", "apairo", "apaireau"], - default_coeff: "", - coeffs: %{ - "" => 1.0, - "+" => 2.0, - "++" => 3.0, - "+++" => 4.0, - "++++" => 5.0, - } - }, - "bière" => - %{ - triggers: ["beer", "bière", "biere", "biaire"], - default_coeff: "25", - coeffs: %{ - "25" => 1.0, - "50" => 2.0, - "75" => 3.0, - "100" => 4.0, - "+" => 2.0, - "++" => 3, - "+++" => 4, - "++++" => 5, - } - }, - "pinard" => %{ - triggers: ["pinard", "vin", "rouge", "blanc", "rosé", "rose"], - annotations: ["rouge", "blanc", "rosé", "rose"], - default_coeff: "", - coeffs: %{ - "" => 1, - "+" => 2, - "++" => 3, - "+++" => 4, - "++++" => 5, - "vase" => 6, - } - }, - "fort" => %{ - triggers: ["shot", "royaume", "whisky", "rhum", "armagnac", "dijo"], - default_coeff: "", - coeffs: %{ - "" => 1, - "+" => 2, - "++" => 3, - "+++" => 4, - "++++" => 5 - } - } - } @user_states %{ :sober => %{ false => ["n'a pas encore bu", "est actuellement sobre"], @@ -149,9 +87,64 @@ defmodule LSG.IRC.AlcoologPlugin do "ZHU NIN JIANKANG", "祝您健康", "SANTÉ", - "CHEERS" + "CHEERS", + "Nuş olsun", + "Živjeli", + "Biba", + "Pura vida", + "Terviseks", + "Mabuhay", + "Kippis", + "Sláinte", + "건배", + "į sveikatą", + "Эрүүл мэндийн төлөө", + "Saúde", + "Sanatate", + "živeli", + "Proscht", + "Chai yo", + "Choc tee", + "Şerefe", + "будьмо", + "Budmo", + "Iechyd da", + "Sei gesund", + "за наш", # Russe original. --kienma + ] - ] + @bad_drinks %{ + :negative => [ + "on peut pas boire en négatif...", + "tu crois que ça marche comme ça ? :s", + "e{{'u' | rrepeat}}h" + ], + :null => [ + "tu me prends pas un peu pour un con là ?", + "zéro ? agis comme un homme et bois une pinte de whisky", + "gros naze", + "FATAL ERROR: java.lang.ArithmeticException (cannot divide by zero)", + "?!?!", + "votre médecin vous conseille de boire une bouteille de rouge cul sec plutôt" + ], + :huge => [ + "tu veux pas aller à l'hopital plutôt ?", + "tu me prends pas un peu pour un con là ?", + "ok; j'ai appelé le SAMU, ils arrivent", + "j'accepterais le !santai quand tu m'auras envoyé la preuve vidéo" + ], + :eau => [ + "Le barman, c'est moi ! C'est moi qui suis barman ! Vous êtes la police républicaine ou une bande ? Vous savez qui je suis ? Enfoncez la bouteille, camarades !", + "L'alcool est notre pire ennemi, fuir est lâche !", + "attention... l'alcool permet de rendre l'eau potable", + "{{'QUOI ?' | bold}}", + "QUO{{'I' | rrepeat}}?", + "{{'COMMENT ÇA DE L'EAU ?' | red}}", + "resaisis toi et va ouvrir une bonne teille de rouge...", + "bwais tu veux pas un \"petit\" rhum plutôt ?" + ] + + } def irc_doc, do: @moduledoc @@ -170,21 +163,16 @@ defmodule LSG.IRC.AlcoologPlugin do end def init(_) do + {:ok, _} = Registry.register(IRC.PubSub, "account", []) {:ok, _} = Registry.register(IRC.PubSub, "trigger:santai", []) + {:ok, _} = Registry.register(IRC.PubSub, "trigger:santo", []) + {:ok, _} = Registry.register(IRC.PubSub, "trigger:santeau", []) {:ok, _} = Registry.register(IRC.PubSub, "trigger:alcoolog", []) {:ok, _} = Registry.register(IRC.PubSub, "trigger:sobre", []) {:ok, _} = Registry.register(IRC.PubSub, "trigger:sobrepour", []) {:ok, _} = Registry.register(IRC.PubSub, "trigger:soif", []) {:ok, _} = Registry.register(IRC.PubSub, "trigger:alcoolisme", []) {:ok, _} = Registry.register(IRC.PubSub, "trigger:alcool", []) - for {_, config} <- @triggers do - for trigger <- config.triggers do - {:ok, _} = Registry.register(IRC.PubSub, "trigger:#{trigger}", []) - for coeff when byte_size(coeff) > 0 <- Map.keys(config.coeffs) do - {:ok, _} = Registry.register(IRC.PubSub, "trigger:#{trigger}#{coeff}", []) - end - end - end dets_filename = (LSG.data_path() <> "/" <> "alcoolisme.dets") |> String.to_charlist {:ok, dets} = :dets.open_file(dets_filename, [{:type,:bag}]) ets = :ets.new(__MODULE__.ETS, [:ordered_set, :named_table, :protected, {:read_concurrency, true}]) @@ -192,42 +180,41 @@ defmodule LSG.IRC.AlcoologPlugin do {:ok, meta} = :dets.open_file(dets_meta_filename, [{:type,:set}]) state = %{dets: dets, meta: meta, ets: ets} - # Upgrade dets format - update_fun = fn(obj, dets) -> + traverse_fun = fn(obj, dets) -> case obj do - object = {nick, date, volumes, name, comment} -> - :dets.delete_object(dets, object) - :dets.insert(dets, {nick, date, volumes || 0.0, 0, name, comment}) - dets object = {nick, date, volumes, active, name, comment} -> - volumes = if is_integer(volumes) do - volumes = volumes+0.0 - :dets.delete_object(dets, object) - :dets.insert(dets, {nick, date, volumes || 0.0, 0, name, comment}) - volumes - else - volumes - end :ets.insert(ets, {{nick, date}, volumes, active, name, comment}) dets _ -> dets end end - :dets.foldl(update_fun, dets, dets) + :dets.foldl(traverse_fun, dets, dets) :dets.sync(dets) + {:ok, state} end + @eau ["santo", "santeau"] + def handle_info({:irc, :trigger, santeau, m = %IRC.Message{trigger: %IRC.Trigger{args: _, type: :bang}}}, state) when santeau in @eau do + m.replyfun.(Tmpl.render(Enum.random(Enum.shuffle(Map.get(@bad_drinks, :eau))), m)) + {:noreply, state} + end + def handle_info({:irc, :trigger, "soif", m = %IRC.Message{trigger: %IRC.Trigger{args: _, type: :bang}}}, state) do now = DateTime.utc_now() |> Timex.Timezone.convert("Europe/Paris") apero = format_duration_from_now(%DateTime{now | hour: 18, minute: 0, second: 0}, false) + day_of_week = Date.day_of_week(now) txt = cond do now.hour >= 0 && now.hour < 6 -> ["apéro tardif ? Je dis OUI ! SANTAI !"] now.hour >= 6 && now.hour < 12 -> - ["C'est quand même un peu tôt non ? Prochain apéro #{apero}"] + if day_of_week >= 6 do + ["C'est quand même un peu tôt non ? Prochain apéro #{apero}"] + else + ["de l'alcool pour le petit dej ? le week-end, pas de problème !"] + end now.hour >= 12 && (now.hour < 14) -> ["oui! c'est l'apéro de midi! (et apéro #{apero})", "tu peux attendre #{apero} ou y aller, il est midi !" @@ -240,9 +227,14 @@ defmodule LSG.IRC.AlcoologPlugin do "préparez les teilles, apéro dans #{apero}!" ] now.hour >= 14 && now.hour < 18 -> - ["tiens bon! apéro #{apero}", - "courage... apéro dans #{apero}", - "pas encore :'( apéro dans #{apero}" + weekend = if day_of_week >= 6 do + " ... ou maintenant en fait, c'est le week-end!" + else + "" + end + ["tiens bon! apéro #{apero}#{weekend}", + "courage... apéro dans #{apero}#{weekend}", + "pas encore :'( apéro dans #{apero}#{weekend}" ] true -> [ @@ -293,8 +285,8 @@ defmodule LSG.IRC.AlcoologPlugin do end if time do - meta = get_user_meta(state, m.sender.nick) - stats = get_full_statistics(state, m.sender.nick) + meta = get_user_meta(state, m.account.id) + stats = get_full_statistics(state, m.account.id) duration = round(DateTime.diff(time, now)/60.0) @@ -318,9 +310,15 @@ defmodule LSG.IRC.AlcoologPlugin do {:noreply, state} end + def handle_info({:irc, :trigger, "alcoolog", m = %IRC.Message{trigger: %IRC.Trigger{args: [], type: :plus}}}, state) do + {:ok, token} = LSG.Token.new({:alcoolog, :index, m.sender.network, m.channel}) + url = LSGWeb.Router.Helpers.alcoolog_url(LSGWeb.Endpoint, :index, m.network, LSGWeb.format_chan(m.channel), token) + m.replyfun.("-> #{url}") + {:noreply, state} + end + def handle_info({:irc, :trigger, "alcoolog", m = %IRC.Message{trigger: %IRC.Trigger{args: [], type: :bang}}}, state) do - {:ok, token} = LSG.Token.new({:alcoolog, :index, m.channel}) - url = LSGWeb.Router.Helpers.alcoolog_url(LSGWeb.Endpoint, :index, token) + url = LSGWeb.Router.Helpers.alcoolog_url(LSGWeb.Endpoint, :index, m.network, LSGWeb.format_chan(m.channel)) m.replyfun.("-> #{url}") {:noreply, state} end @@ -329,7 +327,7 @@ defmodule LSG.IRC.AlcoologPlugin do {cl, _} = Util.float_paparse(cl) {deg, _} = Util.float_paparse(deg) points = Alcool.units(cl, deg) - meta = get_user_meta(state, m.sender.nick) + meta = get_user_meta(state, m.account.id) k = if meta.sex, do: 0.7, else: 0.6 weight = meta.weight gl = (10*points)/(k*weight) @@ -345,41 +343,67 @@ defmodule LSG.IRC.AlcoologPlugin do {:noreply, state} end - def handle_info({:irc, :trigger, "santai", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do - case args do - [cl, deg | comment] -> - comment = if comment == [] do - nil - else - Enum.join(comment, " ") + def handle_info({:irc, :trigger, "santai", m = %IRC.Message{trigger: %IRC.Trigger{args: [cl, deg | comment], type: :bang}}}, state) do + comment = if comment == [] do + nil + else + Enum.join(comment, " ") + end + + {cl, cl_extra} = case {Util.float_paparse(cl), cl} do + {{cl, extra}, _} -> {cl, extra} + {:error, "("<>_} -> + try do + {:ok, result} = Abacus.eval(cl) + {result, nil} + rescue + _ -> {nil, "cl: invalid calc expression"} end + {:error, _} -> {nil, "cl: invalid value"} + end - {cl, _} = Util.float_paparse(cl) - {deg, _} = Util.float_paparse(deg) - points = Alcool.units(cl, deg) + {deg, comment, auto_set, beer_id} = case Util.float_paparse(deg) do + {deg, _} -> {deg, comment, false, nil} + :error -> + beername = if(comment, do: "#{deg} #{comment}", else: deg) + case Untappd.search_beer(beername, limit: 1) do + {:ok, %{"response" => %{"beers" => %{"count" => count, "items" => [%{"beer" => beer, "brewery" => brewery} | _]}}}} -> + {Map.get(beer, "beer_abv"), "#{Map.get(brewery, "brewery_name")}: #{Map.get(beer, "beer_name")}", true, Map.get(beer, "bid")} + _ -> + {deg, "could not find beer", false, nil} + end + end - if cl > 0 && deg > 0 do - now = DateTime.to_unix(DateTime.utc_now(), :millisecond) - user_meta = get_user_meta(state, m.sender.nick) - name = "#{cl}cl #{deg}°" - old_stats = get_full_statistics(state, m.sender.nick) - :ok = :dets.insert(state.dets, {String.downcase(m.sender.nick), now, points, if(old_stats, do: old_stats.active, else: 0), name, comment}) - true = :ets.insert(state.ets, {{String.downcase(m.sender.nick), now}, points, if(old_stats, do: old_stats.active, else: 0),name, comment}) - sante = @santai |> Enum.shuffle() |> Enum.random() - meta = get_user_meta(state, m.sender.nick) - k = if meta.sex, do: 0.7, else: 0.6 - weight = meta.weight - peak = Float.round((10*points||0.0)/(k*weight), 4) - stats = get_full_statistics(state, m.sender.nick) - sober_add = if old_stats && Map.get(old_stats || %{}, :sober_in) do - mins = round(stats.sober_in - old_stats.sober_in) - " [+#{mins}m]" - else - "" - end - nonow = DateTime.utc_now() - sober = nonow |> DateTime.add(round(stats.sober_in*60), :second) - |> Timex.Timezone.convert("Europe/Paris") + + cond do + cl == nil -> m.replyfun.(cl_extra) + deg == nil -> m.replyfun.(comment) + cl >= 500 || deg >= 100 -> m.replyfun.(Tmpl.render(Enum.random(Enum.shuffle(Map.get(@bad_drinks, :huge))), m)) + cl == 0 || deg == 0 -> m.replyfun.(Tmpl.render(Enum.random(Enum.shuffle(Map.get(@bad_drinks, :null))), m)) + cl < 0 || deg < 0 -> m.replyfun.(Tmpl.render(Enum.random(Enum.shuffle(Map.get(@bad_drinks, :negative))), m)) + true -> + points = Alcool.units(cl, deg) + now = DateTime.to_unix(DateTime.utc_now(), :millisecond) + user_meta = get_user_meta(state, m.account.id) + name = "#{cl}cl #{deg}°" + old_stats = get_full_statistics(state, m.account.id) + :ok = :dets.insert(state.dets, {m.account.id, now, points, if(old_stats, do: old_stats.active, else: 0), name, comment}) + true = :ets.insert(state.ets, {{m.account.id, now}, points, if(old_stats, do: old_stats.active, else: 0),name, comment}) + sante = @santai |> Enum.map(fn(s) -> String.trim(String.upcase(s)) end) |> Enum.shuffle() |> Enum.random() + meta = get_user_meta(state, m.account.id) + k = if meta.sex, do: 0.7, else: 0.6 + weight = meta.weight + peak = Float.round((10*points||0.0)/(k*weight), 4) + stats = get_full_statistics(state, m.account.id) + sober_add = if old_stats && Map.get(old_stats || %{}, :sober_in) do + mins = round(stats.sober_in - old_stats.sober_in) + " [+#{mins}m]" + else + "" + end + nonow = DateTime.utc_now() + sober = nonow |> DateTime.add(round(stats.sober_in*60), :second) + |> Timex.Timezone.convert("Europe/Paris") at = if nonow.day == sober.day do {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "aujourd'hui {h24}:{m}", "fr") detail @@ -388,29 +412,139 @@ defmodule LSG.IRC.AlcoologPlugin do detail end - up = if stats.active_drinks > 1 do - " " <> Enum.join(for(_ <- 1..stats.active_drinks, do: "▲")) <> "" - else - "" - end + up = if stats.active_drinks > 1 do + " " <> Enum.join(for(_ <- 1..stats.active_drinks, do: "▲")) <> "" + else + "" + end - m.replyfun.("#{sante} #{m.sender.nick}#{up} #{format_points(points)} @#{stats.active}g/l [+#{peak} g/l]" + msg = fn(nick, extra) -> + "#{sante} #{nick}#{extra}#{up} #{format_points(points)} @#{stats.active}g/l [+#{peak} g/l]" <> " (15m: #{stats.active15m}, 30m: #{stats.active30m}, 1h: #{stats.active1h}) (sobriété #{at} (dans #{stats.sober_in_s})#{sober_add}) !" - <> " (aujourd'hui #{stats.daily_volumes} points - #{stats.daily_gl} g/l)") - else - m.replyfun.("on ne peut pas boire en négatif...") + <> " (aujourd'hui #{stats.daily_volumes} points - #{stats.daily_gl} g/l)" end - _ -> - m.replyfun.("!santai <cl> <degrés> [commentaire]") + + if beer_id do + spawn(fn() -> + case Untappd.maybe_checkin(m.account, beer_id) do + {:ok, body} -> + badges = get_in(body, ["badges", "items"]) + if badges != [] do + badges_s = Enum.map(badges, fn(badge) -> Map.get(badge, "badge_name") end) + |> Enum.filter(fn(b) -> b end) + |> Enum.intersperse(", ") + |> Enum.join("") + badge = if(badges > 1, do: "badges", else: "badge") + m.replyfun.("\\O/ Unlocked untappd #{badge}: #{badges_s}") + end + :ok + {:error, {:http_error, error}} when is_integer(error) -> m.replyfun.("Checkin to Untappd failed: #{to_string(error)}") + {:error, {:http_error, error}} -> m.replyfun.("Checkin to Untappd failed: #{inspect error}") + _ -> :error + end + end) + end + + local_extra = if auto_set, do: " #{comment} (#{deg}°)", else: "" + m.replyfun.(msg.(m.sender.nick, local_extra)) + notify = IRC.Membership.notify_channels(m.account) -- [{m.network,m.channel}] + for {net, chan} <- notify do + user = IRC.UserTrack.find_by_account(net, m.account) + nick = if(user, do: user.nick, else: m.account.name) + extra = " " <> present_type(name, comment) <> "" + IRC.Connection.broadcast_message(net, chan, msg.(nick, extra)) + end + + miss = cond do + stats.active30m >= 2.9 && stats.active30m < 3 -> :miss3 + stats.active30m >= 1.9 && stats.active30m < 2 -> :miss2 + stats.active30m >= 0.9 && stats.active30m < 1 -> :miss1 + stats.active30m >= 0.45 && stats.active30m < 0.5 -> :miss05 + stats.active30m >= 0.20 && stats.active30m < 0.20 -> :miss025 + stats.active30m >= 3 && stats.active1h < 3.15 -> :small3 + stats.active30m >= 2 && stats.active1h < 2.15 -> :small2 + stats.active30m >= 1.5 && stats.active1h < 1.5 -> :small15 + stats.active30m >= 1 && stats.active1h < 1.15 -> :small1 + stats.active30m >= 0.5 && stats.active1h <= 0.51 -> :small05 + stats.active30m >= 0.25 && stats.active30m <= 0.255 -> :small025 + true -> nil + end + + miss = case miss do + :miss025 -> [ + "si peu ?" + ] + :miss05 -> [ + "tu pourras encore conduire, faut boire plus!" + ] + :miss1 -> [ + "alerte, tu vas rater le gramme !" + ] + :miss2 -> [ + "petit joueur ? rater deux grammes de si peu c'est pas sérieux" + ] + :miss3 -> [ + "même pas 3 grammes ? allez ! dernière ligne droite!" + ] + :small025 -> [ + "tu vas quand même pas en rester là si ?" + ] + :small05 -> [ + "c'est un bon début, faut continuer sur cette lancée !" + ] + :small1 -> [ + "un peu plus de motivation sur le gramme et c'est parfait !" + ] + :small15 -> [ + "essaie de garder ton gramme et demi plus longtemps !" + ] + :small2 -> [ + "même pas une demi heure de deux grammes ? allez, fait un effort !", + "installe toi mieux aux deux grammes !" + ] + :small3 -> [ + "ALLAIIIII CONTINUE FAUT MAINTENIR !", + "tu vas quand même pas en rester là ?" + ] + _ -> nil + end + if miss do + msg = Enum.random(Enum.shuffle(miss)) + for {net, chan} <- IRC.Membership.notify_channels(m.account) do + user = IRC.UserTrack.find_by_account(net, m.account) + nick = if(user, do: user.nick, else: m.account.name) + IRC.Connection.broadcast_message(net, chan, "#{nick}: #{msg}") + end + end + end {:noreply, state} end - def get_channel_statistics(channel) do - IRC.UserTrack.to_list() - |> Enum.filter(fn({_, nick, _, _, _, _, channels}) -> Map.get(channels, channel) end) - |> Enum.map(fn({_, nick, _, _, _, _, _}) -> nick end) - |> Enum.map(fn(nick) -> {nick, get_full_statistics(nick)} end) + def handle_info({:irc, :trigger, "santai", m = %IRC.Message{trigger: %IRC.Trigger{args: _, type: :bang}}}, state) do + m.replyfun.("!santai <cl> <degrés> [commentaire]") + {:noreply, state} + end + + def get_all_stats() do + IRC.Account.all_accounts() + |> Enum.map(fn(account) -> {account.id, get_full_statistics(account.id)} end) + |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end) + |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2) + end + + def get_channel_statistics(account, network, nil) do + IRC.Membership.expanded_members_or_friends(account, network, nil) + |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(account.id)} end) + |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end) + |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2) + end + + def get_channel_statistics(_, network, channel), do: get_channel_statistics(network, channel) + + def get_channel_statistics(network, channel) do + IRC.Membership.expanded_members(network, channel) + |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(account.id)} end) |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end) |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2) end @@ -423,8 +557,9 @@ defmodule LSG.IRC.AlcoologPlugin do case get_statistics_for_nick(state, nick) do {count, {_, last_at, last_points, last_active, last_type, last_descr}} -> {active, active_drinks} = current_alcohol_level(state, nick) - {rising, m30} = alcohol_level_rising(state, nick) + {_, m30} = alcohol_level_rising(state, nick) {rising, m15} = alcohol_level_rising(state, nick, 15) + {_, m5} = alcohol_level_rising(state, nick, 5) {_, h1} = alcohol_level_rising(state, nick, 60) trend = if rising do @@ -474,7 +609,7 @@ defmodule LSG.IRC.AlcoologPlugin do %{active: active, last_at: last_at, last_points: last_points, last_type: last_type, last_descr: last_descr, trend_symbol: trend, - active15m: m15, active30m: m30, active1h: h1, + active5m: m5, active15m: m15, active30m: m30, active1h: h1, rising: rising, active_drinks: active_drinks, user_status: user_status, @@ -487,9 +622,8 @@ defmodule LSG.IRC.AlcoologPlugin do end def handle_info({:irc, :trigger, "sobre", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :dot}}}, state) do - nicks = Enum.filter(IRC.UserTrack.to_list, fn({_, nick, _, _, _, _, channels}) -> Map.get(channels, m.channel) end) |> Enum.map(fn({_, nick, _, _, _, _, _}) -> nick end) - nicks = nicks - |> Enum.map(fn(nick) -> {nick, get_full_statistics(state, nick)} end) + nicks = IRC.Membership.expanded_members_or_friends(m.account, m.network, m.channel) + |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(state, account.id)} end) |> Enum.filter(fn({_nick, status}) -> status && status.sober_in && status.sober_in > 0 end) |> Enum.sort_by(fn({_, status}) -> status.sober_in end, &</2) |> Enum.map(fn({nick, stats}) -> @@ -518,34 +652,39 @@ defmodule LSG.IRC.AlcoologPlugin do end def handle_info({:irc, :trigger, "sobre", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do - nick = case args do - [nick] -> nick - [] -> m.sender.nick + account = case args do + [nick] -> IRC.Account.find_always_by_nick(m.network, m.channel, nick) + [] -> m.account end - stats = get_full_statistics(state, nick) - if stats && stats.sober_in > 0 do - now = DateTime.utc_now() - sober = now |> DateTime.add(round(stats.sober_in*60), :second) - |> Timex.Timezone.convert("Europe/Paris") - at = if now.day == sober.day do - {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "aujourd'hui {h24}:{m}", "fr") - detail - else - {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr") - detail - end - m.replyfun.("#{nick} sera sobre #{at} (dans #{stats.sober_in_s})!") + if account do + user = IRC.UserTrack.find_by_account(m.network, account) + nick = if(user, do: user.nick, else: account.name) + stats = get_full_statistics(state, nick) + if stats && stats.sober_in > 0 do + now = DateTime.utc_now() + sober = now |> DateTime.add(round(stats.sober_in*60), :second) + |> Timex.Timezone.convert("Europe/Paris") + at = if now.day == sober.day do + {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "aujourd'hui {h24}:{m}", "fr") + detail + else + {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr") + detail + end + m.replyfun.("#{nick} sera sobre #{at} (dans #{stats.sober_in_s})!") + else + m.replyfun.("#{nick} est déjà sobre. aidez le !") + end else - m.replyfun.("#{nick} est déjà sobre. aidez le !") + m.replyfun.("inconnu") end {:noreply, state} end def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: [], type: :dot}}}, state) do - nicks = Enum.filter(IRC.UserTrack.to_list, fn({_, nick, _, _, _, _, channels}) -> Map.get(channels, m.channel) end) |> Enum.map(fn({_, nick, _, _, _, _, _}) -> nick end) - nicks = nicks - |> Enum.map(fn(nick) -> {nick, get_full_statistics(state, nick)} end) + nicks = IRC.Membership.expanded_members_or_friends(m.account, m.network, m.channel) + |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(state, account.id)} end) |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end) |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2) |> Enum.map(fn({nick, status}) -> @@ -554,7 +693,7 @@ defmodule LSG.IRC.AlcoologPlugin do else status.trend_symbol end - "#{nick} #{status.user_status} #{trend_symbol} #{status.active} g/l" + "#{nick} #{status.user_status} #{trend_symbol} #{Float.round(status.active, 4)} g/l" end) |> Enum.intersperse(", ") |> Enum.join("") @@ -569,21 +708,116 @@ defmodule LSG.IRC.AlcoologPlugin do {:noreply, state} end + def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: [time], type: :dot}}}, state) do + time = case time do + "semaine" -> 7 + string -> + case Integer.parse(string) do + {time, "j"} -> time + {time, "J"} -> time + _ -> nil + end + end + + if time do + aday = time*((24 * 60)*60) + now = DateTime.utc_now() + before = now + |> DateTime.add(-aday, :second) + |> DateTime.to_unix(:millisecond) + over_time_stats(before, time, m, state) + else + m.replyfun.(".alcooolisme semaine|Xj") + end + {:noreply, state} + end + + # TODO Fix with user/channel membership def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: ["semaine"], type: :bang}}}, state) do aday = 7*((24 * 60)*60) now = DateTime.utc_now() before = now |> DateTime.add(-aday, :second) - |> DateTime.to_unix(:millisecond) + |> DateTime.to_unix(:millisecond) + over_time_stats(before, 7, m, state) + end + + def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: ["30j"], type: :bang}}}, state) do + aday = 31*((24 * 60)*60) + now = DateTime.utc_now() + before = now + |> DateTime.add(-aday, :second) + |> DateTime.to_unix(:millisecond) + over_time_stats(before, 30, m, state) + end + + def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: ["90j"], type: :bang}}}, state) do + aday = 91*((24 * 60)*60) + now = DateTime.utc_now() + before = now + |> DateTime.add(-aday, :second) + |> DateTime.to_unix(:millisecond) + over_time_stats(before, 90, m, state) + end + + def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: ["180j"], type: :bang}}}, state) do + aday = 180*((24 * 60)*60) + now = DateTime.utc_now() + before = now + |> DateTime.add(-aday, :second) + |> DateTime.to_unix(:millisecond) + over_time_stats(before, 180, m, state) + end + + + def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: ["365j"], type: :bang}}}, state) do + aday = 365*((24 * 60)*60) + now = DateTime.utc_now() + before = now + |> DateTime.add(-aday, :second) + |> DateTime.to_unix(:millisecond) + over_time_stats(before, 365, m, state) + end + + defp user_over_time(state, account, count) do + delay = count*((24 * 60)*60) + now = DateTime.utc_now() + before = DateTime.utc_now() + |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase) + |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase) + |> DateTime.to_unix(:millisecond) + #[ +# {{{:"$1", :"$2"}, :_, :_, :_, :_}, +# [{:andalso, {:==, :"$1", :"$1"}, {:<, :"$2", {:const, 3000}}}], [:lol]} + #] + match = [{{{:"$1", :"$2"}, :_, :_, :_, :_}, + [{:andalso, {:>, :"$2", {:const, before}}, {:==, :"$1", {:const, account.id}}}], [:"$_"]} + ] + :ets.select(state.ets, match) + |> Enum.reduce(Map.new, fn({{_, ts}, vol, _, _, _}, acc) -> + date = DateTime.from_unix!(ts, :millisecond) + |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase) + + date = if date.hour <= 8 do + DateTime.add(date, -(60*(60*(date.hour+1))), :second, Tzdata.TimeZoneDatabase) + else + date + end + |> DateTime.to_date() + + Map.put(acc, date, Map.get(acc, date, 0) + vol) + end) + end + + defp over_time_stats(before, j, m, state) do #match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end) - match = [ - {{{:_, :"$1"}, :_, :_, :_, :_}, - [ - {:>, :"$1", {:const, before}}, - ], [:"$_"]} + match = [{{{:_, :"$1"}, :_, :_, :_, :_}, + [{:>, :"$1", {:const, before}}], [:"$_"]} ] # tuple ets: {{nick, date}, volumes, current, nom, commentaire} + members = IRC.Membership.members_or_friends(m.account, m.network, m.channel) drinks = :ets.select(state.ets, match) + |> Enum.filter(fn({{account, _}, _, _, _, _}) -> Enum.member?(members, account) end) |> Enum.sort_by(fn({{_, ts}, _, _, _, _}) -> ts end, &>/2) top = Enum.reduce(drinks, %{}, fn({{nick, _}, vol, _, _, _}, acc) -> @@ -591,15 +825,20 @@ defmodule LSG.IRC.AlcoologPlugin do Map.put(acc, nick, all + vol) end) |> Enum.sort_by(fn({_nick, count}) -> count end, &>/2) - |> Enum.map(fn({nick, count}) -> "#{nick}: #{Float.round(count, 4)}" end) + |> Enum.map(fn({nick, count}) -> + account = IRC.Account.get(nick) + user = IRC.UserTrack.find_by_account(m.network, account) + nick = if(user, do: user.nick, else: account.name) + "#{nick}: #{Float.round(count, 4)}" + end) |> Enum.intersperse(", ") - m.replyfun.("sur 7 jours: #{top}") + m.replyfun.("sur #{j} jours: #{top}") {:noreply, state} end def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: [], type: :plus}}}, state) do - meta = get_user_meta(state, m.sender.nick) + meta = get_user_meta(state, m.account.id) hf = if meta.sex, do: "h", else: "f" m.replyfun.("+alcoolisme sexe: #{hf} poids: #{meta.weight} facteur de perte: #{meta.loss_factor}") {:noreply, state} @@ -629,13 +868,15 @@ defmodule LSG.IRC.AlcoologPlugin do if h == nil || weight == nil do m.replyfun.("paramètres invalides") else - old_meta = get_user_meta(state, m.sender.nick) + old_meta = get_user_meta(state, m.account.id) meta = Map.merge(@default_user_meta, %{sex: h, weight: weight, loss_factor: factor}) - put_user_meta(state, m.sender.nick, meta) - msg = if old_meta.weight < meta.weight do - "t'as grossi..." - else - "ok" + put_user_meta(state, m.account.id, meta) + fat = ["t'as grossi...", "bientôt la tonne ?", "t'as encore abusé du fromage ?"] + thin = ["en route vers l'anorexie ?", "t'as grossi...", "faut manger plus de fromage!"] + msg = cond do + old_meta.weight < meta.weight -> Enum.random(Enum.shuffle(fat)) + old_meta.weight == meta.weight -> "ok" + true -> Enum.random(Enum.shuffle(thin)) end m.replyfun.(msg) end @@ -644,12 +885,18 @@ defmodule LSG.IRC.AlcoologPlugin do end def handle_info({:irc, :trigger, "santai", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :minus}}}, state) do - case get_statistics_for_nick(state, m.sender.nick) do + case get_statistics_for_nick(state, m.account.id) do {_, obj = {_, date, points, _last_active, type, descr}} -> :dets.delete_object(state.dets, obj) - :ets.delete(state.ets, {String.downcase(m.sender.nick), date}) + :ets.delete(state.ets, {m.account.id, date}) m.replyfun.("supprimé: #{m.sender.nick} #{points} #{type} #{descr}") m.replyfun.("faudrait quand même penser à boire") + notify = IRC.Membership.notify_channels(m.account) -- [{m.network,m.channel}] + for {net, chan} <- notify do + user = IRC.UserTrack.find_by_account(net, m.account) + nick = if(user, do: user.nick, else: m.account.name) + IRC.Connection.broadcast_message(net, chan, "#{nick} -santai #{points} #{type} #{descr}") + end {:noreply, state} _ -> {:noreply, state} @@ -657,61 +904,134 @@ defmodule LSG.IRC.AlcoologPlugin do end def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do - nick = case args do - [nick] -> nick - [] -> m.sender.nick + {account, duration} = case args do + [nick | rest] -> {IRC.Account.find_always_by_nick(m.network, m.channel, nick), rest} + [] -> {m.account, []} end - if stats = get_full_statistics(state, nick) do - trend_symbol = if stats.active_drinks > 1 do - Enum.join(for(_ <- 1..stats.active_drinks, do: stats.trend_symbol)) + if account do + duration = case duration do + ["semaine"] -> 7 + [j] -> + case Integer.parse(j) do + {j, "j"} -> j + _ -> nil + end + _ -> nil + end + user = IRC.UserTrack.find_by_account(m.network, account) + nick = if(user, do: user.nick, else: account.name) + if duration do + if duration > 90 do + m.replyfun.("trop gros, ça rentrera pas") else - stats.trend_symbol + # duration stats + stats = user_over_time(state, account, duration) + |> Enum.sort_by(fn({k,_v}) -> k end, {:asc, Date}) + |> Enum.map(fn({date, count}) -> + "#{date.day}: #{Float.round(count, 2)}" + end) + |> Enum.intersperse(", ") + |> Enum.join("") + + if stats == "" do + m.replyfun.("alcoolisme a zéro sur #{duration}j :/") + else + m.replyfun.("alcoolisme de #{nick}, #{duration} derniers jours: #{stats}") + end end - msg = "#{nick} #{stats.user_status} " - <> (if stats.active > 0 || stats.active15m > 0 || stats.active30m > 0 || stats.active1h > 0, do: ": #{trend_symbol} #{stats.active}g/l ", else: "") - <> (if stats.active30m > 0 || stats.active1h > 0, do: "(15m: #{stats.active15m}, 30m: #{stats.active30m}, 1h: #{stats.active1h}) ", else: "") - <> (if stats.sober_in > 0, do: "— Sobre dans #{stats.sober_in_s} ", else: "") - <> "— Dernier verre: #{present_type(stats.last_type, stats.last_descr)} [#{Float.round(stats.last_points+0.0, 4)}] " - <> "#{format_duration_from_now(stats.last_at)} " - <> (if stats.daily_volumes > 0, do: "— Aujourd'hui: #{stats.daily_volumes} #{stats.daily_gl}g/l", else: "") - - m.replyfun.(msg) - else - m.replyfun.("honteux mais #{nick} n'a pas l'air alcoolique du tout. /kick") + else + if stats = get_full_statistics(state, account.id) do + trend_symbol = if stats.active_drinks > 1 do + Enum.join(for(_ <- 1..stats.active_drinks, do: stats.trend_symbol)) + else + stats.trend_symbol + end + # TODO: Lookup nick for account_id + msg = "#{nick} #{stats.user_status} " + <> (if stats.active > 0 || stats.active15m > 0 || stats.active30m > 0 || stats.active1h > 0, do: ": #{trend_symbol} #{Float.round(stats.active, 4)}g/l ", else: "") + <> (if stats.active30m > 0 || stats.active1h > 0, do: "(15m: #{stats.active15m}, 30m: #{stats.active30m}, 1h: #{stats.active1h}) ", else: "") + <> (if stats.sober_in > 0, do: "— Sobre dans #{stats.sober_in_s} ", else: "") + <> "— Dernier verre: #{present_type(stats.last_type, stats.last_descr)} [#{Float.round(stats.last_points+0.0, 4)}] " + <> "#{format_duration_from_now(stats.last_at)} " + <> (if stats.daily_volumes > 0, do: "— Aujourd'hui: #{stats.daily_volumes} #{stats.daily_gl}g/l", else: "") + + m.replyfun.(msg) + else + m.replyfun.("honteux mais #{nick} n'a pas l'air alcoolique du tout. /kick") + end + end + else + m.replyfun.("je ne connais pas cet utilisateur") end {:noreply, state} end - for {name, config} <- @triggers do - coeffs = Map.get(config, "coeffs") - default_coeff_value = Map.get(config.coeffs, config.default_coeff) - + # Account merge + 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) -> + Logger.debug("alcolog/account_change:: merging #{old_id} -> #{new_id}") + rename_object_owner(table, state.ets, obj, old_id, new_id) + end) + case :dets.lookup(state.meta, {:meta, old_id}) do + [{_, meta}] -> + :dets.delete(state.meta, {:meta, old_id}) + :dets.insert(state.meta, {{:meta, new_id}, meta}) + _ -> + :ok + end + {:noreply, state} + end - #IO.puts "at triggers #{inspect config}" - # Handle each trigger - for trigger <- config.triggers do + def terminate(_, state) do + for dets <- [state.dets, state.meta] do + :dets.sync(dets) + :dets.close(dets) + end + end - # … with a known coeff … - for {coef, value} when byte_size(coef) > 0 <- config.coeffs do - def handle_info({:irc, :trigger, unquote(trigger), m = %IRC.Message{trigger: %IRC.Trigger{args: [unquote(coef)<>_ | args], type: :bang}}}, state) do - handle_bang(unquote(name), unquote(value), m, args, state) - {:noreply, state} - end - def handle_info({:irc, :trigger, unquote(trigger)<>unquote(coef), m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do - handle_bang(unquote(name), unquote(value), m, args, state) - {:noreply, state} - end - end + defp rename_object_owner(table, ets, object = {old_id, date, volume, current, name, comment}, old_id, new_id) do + :dets.delete_object(table, object) + :ets.delete(ets, {old_id, date}) + :dets.insert(table, {new_id, date, volume, current, name, comment}) + :ets.insert(ets, {{new_id, date}, volume, current, name, comment}) + end - # … or without - def handle_info({:irc, :trigger, unquote(trigger), m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do - handle_bang(unquote(name), unquote(default_coeff_value), m, args, state) - {:noreply, state} + # Account: move from nick to account id + def handle_info({:accounts, accounts}, state) do + #for x={:account, _, _, _, _} <- accounts, do: handle_info(x, state) + #{:noreply, state} + mapping = Enum.reduce(accounts, Map.new, fn({:account, _net, _chan, nick, account_id}, acc) -> + Map.put(acc, String.downcase(nick), account_id) + end) + spec = [{{:"$1", :_, :_, :_, :_, :_}, [], [:"$_"]}] + Logger.debug("accounts:: mappings #{inspect mapping}") + Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj = {nick, _date, _vol, _cur, _name, _comment}) -> + #Logger.debug("accounts:: item #{inspect(obj)}") + if new_id = Map.get(mapping, nick) do + Logger.debug("alcolog/accounts:: merging #{nick} -> #{new_id}") + rename_object_owner(table, state.ets, obj, nick, new_id) end + 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("alcoolog/account:: merging #{nick} -> #{account_id}") + rename_object_owner(table, state.ets, obj, nick, account_id) + end) + case :dets.lookup(state.meta, {:meta, nick}) do + [{_, meta}] -> + :dets.delete(state.meta, {:meta, nick}) + :dets.insert(state.meta, {{:meta, account_id}, meta}) + _ -> + :ok end - + {:noreply, state} end def handle_info(t, state) do @@ -719,35 +1039,8 @@ defmodule LSG.IRC.AlcoologPlugin do {:noreply, state} end - defp handle_bang(name, points, message, args, state) do - description = case args do - [] -> nil - [something] -> something - something when is_list(something) -> Enum.join(something, " ") - _ -> nil - end - now = DateTime.to_unix(DateTime.utc_now(), :milliseconds) - points = points+0.0 - user_meta = get_user_meta(state, message.sender.nick) - # TODO: Calculer la perte sur last_active depuis last_at. - # TODO: Ajouter les g/L de la nouvelle boisson. - :ok = :dets.insert(state.dets, {String.downcase(message.sender.nick), now, points, 0, name, description}) - true = :ets.insert(state.ets, {{String.downcase(message.sender.nick), now}, points, 0, name, description}) - {active,_} = current_alcohol_level(state, message.sender.nick) - {count, {_, last_at, last_points, _active, last_type, last_descr}} = get_statistics_for_nick(state, message.sender.nick) - sante = @santai |> Enum.shuffle() |> Enum.random() - k = if user_meta.sex, do: 0.7, else: 0.6 - weight = user_meta.weight - peak = (10*points)/(k*weight) - {_, m30} = alcohol_level_rising(state, message.sender.nick) - {_, m15} = alcohol_level_rising(state, message.sender.nick, 15) - {_, h1} = alcohol_level_rising(state, message.sender.nick, 60) - #message.replyfun.("#{sante} #{message.sender.nick} #{format_points(points)}! #{active}~ g/L (total #{Float.round(count+0.0, 4)} points)") - message.replyfun.("#{sante} #{message.sender.nick} #{format_points(points)} @#{active}~ g/l [+#{Float.round(peak+0.0, 4)} g/l] (15m: #{m15}, 30m: #{m30}, 1h: #{h1})! (total #{Float.round(count+0.0, 4)} points)") - end - - defp get_statistics_for_nick(state, nick) do - qvc = :dets.lookup(state.dets, String.downcase(nick)) + defp get_statistics_for_nick(state, account_id) do + qvc = :dets.lookup(state.dets, account_id) |> Enum.sort_by(fn({_, ts, _, _, _, _}) -> ts end, &</2) count = Enum.reduce(qvc, 0, fn({_nick, _ts, points, _active, _type, _descr}, acc) -> acc + (points||0) end) last = List.last(qvc) || nil @@ -785,13 +1078,13 @@ defmodule LSG.IRC.AlcoologPlugin do relative <> detail end - defp put_user_meta(state, nick, meta) do - :dets.insert(state.meta, {{:meta, String.downcase(nick)}, meta}) + defp put_user_meta(state, account_id, meta) do + :dets.insert(state.meta, {{:meta, account_id}, meta}) :ok end - defp get_user_meta(%{meta: meta}, nick) do - case :dets.lookup(meta, {:meta, String.downcase(nick)}) do + defp get_user_meta(%{meta: meta}, account_id) do + case :dets.lookup(meta, {:meta, account_id}) do [{{:meta, _}, meta}] -> Map.merge(@default_user_meta, meta) _ -> @@ -809,8 +1102,8 @@ defmodule LSG.IRC.AlcoologPlugin do # stop folding when ? # - defp user_stats(state = %{ets: ets}, nick) do - meta = get_user_meta(state, nick) + defp user_stats(state = %{ets: ets}, account_id) do + meta = get_user_meta(state, account_id) aday = (10 * 60)*60 now = DateTime.utc_now() before = now @@ -818,12 +1111,12 @@ defmodule LSG.IRC.AlcoologPlugin do |> DateTime.to_unix(:millisecond) #match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end) match = [ - {{{:"$1", :"$2"}, :_, :_, :_, :_}, - [ - {:>, :"$2", {:const, before}}, - {:"=:=", {:const, String.downcase(nick)}, :"$1"} - ], [:"$_"]} -] + {{{:"$1", :"$2"}, :_, :_, :_, :_}, + [ + {:>, :"$2", {:const, before}}, + {:"=:=", {:const, account_id}, :"$1"} + ], [:"$_"]} + ] # tuple ets: {{nick, date}, volumes, current, nom, commentaire} drinks = :ets.select(ets, match) # {date, single_peak} @@ -836,11 +1129,11 @@ defmodule LSG.IRC.AlcoologPlugin do {Float.round(total_volume + 0.0, 4), Float.round(gl + 0.0, 4)} end - defp alcohol_level_rising(state, nick, minutes \\ 30) do - {now, _} = current_alcohol_level(state, nick) + defp alcohol_level_rising(state, account_id, minutes \\ 30) do + {now, _} = current_alcohol_level(state, account_id) soon_date = DateTime.utc_now |> DateTime.add(minutes*60, :second) - {soon, _} = current_alcohol_level(state, nick, soon_date) + {soon, _} = current_alcohol_level(state, account_id, soon_date) soon = cond do soon < 0 -> 0.0 true -> soon @@ -849,8 +1142,8 @@ defmodule LSG.IRC.AlcoologPlugin do {soon > now, Float.round(soon+0.0, 4)} end - defp current_alcohol_level(state = %{ets: ets}, nick, now \\ nil) do - meta = get_user_meta(state, nick) + defp current_alcohol_level(state = %{ets: ets}, account_id, now \\ nil) do + meta = get_user_meta(state, account_id) aday = ((24*7) * 60)*60 now = if now do now @@ -860,15 +1153,14 @@ defmodule LSG.IRC.AlcoologPlugin do before = now |> DateTime.add(-aday, :second) |> DateTime.to_unix(:millisecond) - nick = String.downcase(nick) #match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end) match = [ - {{{:"$1", :"$2"}, :_, :_, :_, :_}, - [ - {:>, :"$2", {:const, before}}, - {:"=:=", {:const, nick}, :"$1"} - ], [:"$_"]} -] + {{{:"$1", :"$2"}, :_, :_, :_, :_}, + [ + {:>, :"$2", {:const, before}}, + {:"=:=", {:const, account_id}, :"$1"} + ], [:"$_"]} + ] # tuple ets: {{nick, date}, volumes, current, nom, commentaire} drinks = :ets.select(ets, match) |> Enum.sort_by(fn({{_, date}, _, _, _, _}) -> date end, &</2) @@ -880,7 +1172,7 @@ defmodule LSG.IRC.AlcoologPlugin do date = DateTime.from_unix!(date, :millisecond) last_at = last_at || date mins_since = round(DateTime.diff(now, date)/60.0) - IO.puts "Drink: #{inspect({date, volume})} - mins since: #{inspect mins_since} - last drink at #{inspect last_at}" + #IO.puts "Drink: #{inspect({date, volume})} - mins since: #{inspect mins_since} - last drink at #{inspect last_at}" # Apply loss since `last_at` on `all` # all = if last_at do @@ -898,7 +1190,7 @@ defmodule LSG.IRC.AlcoologPlugin do if mins_since < 30 do per_min = (peak)/30.0 current = (per_min*mins_since) - IO.puts "Applying current drink 30m: from #{peak}, loss of #{inspect per_min}/min (mins since #{inspect mins_since})" + #IO.puts "Applying current drink 30m: from #{peak}, loss of #{inspect per_min}/min (mins since #{inspect mins_since})" {all + current, date, [{date, current} | acc], active_drinks + 1} else {all + peak, date, [{date, peak} | acc], active_drinks} |