summaryrefslogtreecommitdiff
path: root/lib/lsg_irc/alcolog_plugin.ex
diff options
context:
space:
mode:
authorhref <href@random.sh>2020-07-07 21:39:10 +0200
committerhref <href@random.sh>2020-07-07 21:39:51 +0200
commitd6ee134a5957e299c3ad59011df320b3c41e6e61 (patch)
tree29567e6635466f8a3415a935b3cc8a777019f5bc /lib/lsg_irc/alcolog_plugin.ex
parentbleh (diff)
pouet
Diffstat (limited to 'lib/lsg_irc/alcolog_plugin.ex')
-rw-r--r--lib/lsg_irc/alcolog_plugin.ex872
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}