summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorhref <href@random.sh>2021-09-01 10:30:18 +0200
committerhref <href@random.sh>2021-09-01 10:30:18 +0200
commit75687711f35355bc30e4829439384aab28fcac6d (patch)
tree8f3256f472893c39720a684d390e890a152f7303 /lib
parentlink: post_* callbacks; html & pdftitle. (diff)
Commit all the changes that hasn't been committed + updates.
Diffstat (limited to 'lib')
-rw-r--r--lib/irc.ex3
-rw-r--r--lib/irc/account.ex3
-rw-r--r--lib/irc/connection.ex11
-rw-r--r--lib/irc/plugin_supervisor.ex14
-rw-r--r--lib/lsg/application.ex2
-rw-r--r--lib/lsg/telegram.ex17
-rw-r--r--lib/lsg_irc.ex9
-rw-r--r--lib/lsg_irc/alcolog_plugin.ex358
-rw-r--r--lib/lsg_irc/alcoolog_announcer_plugin.ex57
-rw-r--r--lib/lsg_irc/base_plugin.ex12
-rw-r--r--lib/lsg_irc/bourosama_plugin.ex7
-rw-r--r--lib/lsg_irc/calc_plugin.ex5
-rw-r--r--lib/lsg_irc/coronavirus_plugin.ex14
-rw-r--r--lib/lsg_irc/correction_plugin.ex8
-rw-r--r--lib/lsg_irc/dice_plugin.ex4
-rw-r--r--lib/lsg_irc/finance_plugin.ex13
-rw-r--r--lib/lsg_irc/kick_roulette_plugin.ex4
-rw-r--r--lib/lsg_irc/last_fm_plugin.ex9
-rw-r--r--lib/lsg_irc/link_plugin.ex11
-rw-r--r--lib/lsg_irc/np_handler.ex4
-rw-r--r--lib/lsg_irc/outline_plugin.ex38
-rw-r--r--lib/lsg_irc/preums_plugin.ex80
-rw-r--r--lib/lsg_irc/quatre_cent_vingt_plugin.ex7
-rw-r--r--lib/lsg_irc/say_plugin.ex9
-rw-r--r--lib/lsg_irc/script_plugin.ex5
-rw-r--r--lib/lsg_irc/seen_plugin.ex7
-rw-r--r--lib/lsg_irc/sms_plugin.ex5
-rw-r--r--lib/lsg_irc/tell_plugin.ex93
-rw-r--r--lib/lsg_irc/txt_plugin.ex16
-rw-r--r--lib/lsg_irc/untappd_plugin.ex2
-rw-r--r--lib/lsg_irc/wikipedia_plugin.ex4
-rw-r--r--lib/lsg_irc/wolfram_alpha_plugin.ex4
-rw-r--r--lib/lsg_irc/youtube_plugin.ex5
-rw-r--r--lib/lsg_web.ex9
-rw-r--r--lib/lsg_web/controllers/alcoolog_controller.ex263
-rw-r--r--lib/lsg_web/controllers/irc_controller.ex9
-rw-r--r--lib/lsg_web/router.ex11
-rw-r--r--lib/lsg_web/templates/alcoolog/index.html.eex222
-rw-r--r--lib/lsg_web/templates/alcoolog/user.html.eex170
-rw-r--r--lib/lsg_web/templates/irc/index.html.eex25
-rw-r--r--lib/lsg_web/templates/irc/txt.html.eex22
-rw-r--r--lib/lsg_web/templates/irc/txts.html.eex35
-rw-r--r--lib/lsg_web/templates/layout/app.html.eex135
-rw-r--r--lib/lsg_web/templates/page/user.html.eex2
-rw-r--r--lib/lsg_web/views/alcoolog_view.ex2
-rw-r--r--lib/lsg_web/views/layout_view.ex7
-rw-r--r--lib/tmpl.ex1
-rw-r--r--lib/util.ex6
48 files changed, 1424 insertions, 335 deletions
diff --git a/lib/irc.ex b/lib/irc.ex
index 1e1fd50..ee44f37 100644
--- a/lib/irc.ex
+++ b/lib/irc.ex
@@ -8,7 +8,8 @@ defmodule IRC do
:sender,
:channel,
:trigger,
- :replyfun]
+ :replyfun,
+ :at]
end
defmodule Trigger do
defstruct [:type, :trigger, :args]
diff --git a/lib/irc/account.ex b/lib/irc/account.ex
index 6f9eb05..0aa8638 100644
--- a/lib/irc/account.ex
+++ b/lib/irc/account.ex
@@ -19,7 +19,8 @@ defmodule IRC.Account do
# FIXME: Ensure uniqueness of name?
defstruct [:id, :name, :token]
- @type t :: %__MODULE__{id: String.t(), name: String.t()}
+ @type t :: %__MODULE__{id: id(), name: String.t()}
+ @type id :: String.t()
defimpl Inspect, for: __MODULE__ do
import Inspect.Algebra
diff --git a/lib/irc/connection.ex b/lib/irc/connection.ex
index d115d88..b83c4d3 100644
--- a/lib/irc/connection.ex
+++ b/lib/irc/connection.ex
@@ -211,13 +211,13 @@ defmodule IRC.Connection do
else
Logger.info("Connecting")
{:ok, client} = ExIRC.Client.start_link(debug: false)
+ ExIRC.Client.add_handler(client, self())
client
end
- ExIRC.Client.add_handler(client, self())
if state.conn.tls do
- ExIRC.Client.connect_ssl!(client, state.conn.host, state.conn.port)
+ ExIRC.Client.connect_ssl!(client, state.conn.host, state.conn.port, [])#[{:ifaddr, {45,150,150,33}}])
else
- ExIRC.Client.connect!(client, state.conn.host, state.conn.port)
+ ExIRC.Client.connect!(client, state.conn.host, state.conn.port, [])#[{:ifaddr, {45,150,150,33}}])
end
{:noreply, %{state | client: client}}
end
@@ -241,8 +241,9 @@ defmodule IRC.Connection do
# Connection successful
def handle_info({:connected, server, port}, state) do
Logger.info("#{inspect(self())} Connected to #{server}:#{port} #{inspect state}")
+ {_, backoff} = :backoff.succeed(state.backoff)
ExIRC.Client.logon(state.client, state.conn.pass || "", state.conn.nick, state.conn.user, state.conn.name)
- {:noreply, state}
+ {:noreply, %{state | backoff: backoff}}
end
# Logon successful
@@ -344,7 +345,7 @@ defmodule IRC.Connection do
end
def handle_info({:parted, channel, %ExIRC.SenderInfo{nick: nick}}, state) do
- IRC.UserTrack.parted(channel, nick)
+ IRC.UserTrack.parted(network(state), channel, nick)
{:noreply, state}
end
diff --git a/lib/irc/plugin_supervisor.ex b/lib/irc/plugin_supervisor.ex
index ca092dc..5f93f17 100644
--- a/lib/irc/plugin_supervisor.ex
+++ b/lib/irc/plugin_supervisor.ex
@@ -3,15 +3,24 @@ defmodule IRC.Plugin do
defmodule Supervisor do
use DynamicSupervisor
+ require Logger
def start_link() do
DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__)
end
def start_child(module, opts \\ []) do
- IO.inspect(module)
+ Logger.info("Starting #{module}")
spec = %{id: {IRC.Plugin,module}, start: {IRC.Plugin, :start_link, [module, opts]}, name: module, restart: :transient}
- DynamicSupervisor.start_child(__MODULE__, spec)
+ case DynamicSupervisor.start_child(__MODULE__, spec) do
+ {:ok, _} = res -> res
+ :ignore ->
+ Logger.info("Ignored #{module}")
+ :ignore
+ {:error,_} = res ->
+ Logger.error("Could not start #{module}: #{inspect(res, pretty: true)}")
+ res
+ end
end
@impl true
@@ -88,4 +97,3 @@ defmodule IRC.Plugin do
end
end
-
diff --git a/lib/lsg/application.ex b/lib/lsg/application.ex
index a12253b..3e5fb41 100644
--- a/lib/lsg/application.ex
+++ b/lib/lsg/application.ex
@@ -25,7 +25,7 @@ defmodule LSG.Application do
# for other strategies and supported options
opts = [strategy: :one_for_one, name: LSG.Supervisor]
sup = Supervisor.start_link(children, opts)
- LSG.IRC.after_start()
+ spawn_link(fn() -> LSG.IRC.after_start() end)
sup
end
diff --git a/lib/lsg/telegram.ex b/lib/lsg/telegram.ex
index 7541b13..02d7115 100644
--- a/lib/lsg/telegram.ex
+++ b/lib/lsg/telegram.ex
@@ -3,8 +3,8 @@ defmodule LSG.Telegram do
use Telegram.Bot,
token: Keyword.get(Application.get_env(:lsg, :telegram, []), :key),
- username: Keyword.get(Application.get_env(:lsg, :telegram), :nick, "beauttebot"),
- purge: true
+ username: Keyword.get(Application.get_env(:lsg, :telegram, []), :nick, "beauttebot"),
+ purge: false
def my_path() do
"https://t.me/beauttebot"
@@ -26,10 +26,10 @@ defmodule LSG.Telegram do
_ -> "nil"
end
- #Handled message "1247435154:AAGnSSCnySn0RuVxy_SUcDEoOX_rbF6vdq0" %{"message" =>
+ #Handled message "1247435154:AAGnSSCnySn0RuVxy_SUcDEoOX_rbF6vdq0" %{"message" =>
# %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"},
# "date" => 1591027272, "entities" =>
- # [%{"length" => 7, "offset" => 0, "type" => "bot_command"}],
+ # [%{"length" => 7, "offset" => 0, "type" => "bot_command"}],
# "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"},
# "message_id" => 11, "text" => "/enable salope"}, "update_id" => 764148578}
account = IRC.Account.find_meta_account("telegram-validation-code", String.downcase(key))
@@ -47,7 +47,7 @@ defmodule LSG.Telegram do
send_message(m["chat"]["id"], text)
end
- #[debug] Unhandled update: %{"message" =>
+ #[debug] Unhandled update: %{"message" =>
# %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"},
# "date" => 1591096015,
# "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"},
@@ -155,7 +155,7 @@ defmodule LSG.Telegram do
s3req = ExAws.S3.put_object(bucket, s3path, body, acl: :public_read, content_type: magic.mime_type),
{:ok, _} <- ExAws.request(s3req)
do
- path = "https://s3.wasabisys.com/#{bucket}/#{s3path}"
+ path = LSGWeb.Router.Helpers.url(LSGWeb.Endpoint) <> "/files/#{s3path}"
sent = for {net, chan} <- target do
user = IRC.UserTrack.find_by_account(net, account)
nick = if(user, do: user.nick, else: account.name)
@@ -210,7 +210,8 @@ defmodule LSG.Telegram do
account: account,
sender: %ExIRC.SenderInfo{nick: account.name},
replyfun: reply_fun,
- trigger: IRC.Connection.extract_trigger(trigger_text)
+ trigger: IRC.Connection.extract_trigger(trigger_text),
+ at: nil
}
IO.puts("converted telegram to message: #{inspect message}")
IRC.Connection.publish(message, ["message:private", "message:telegram"])
@@ -250,5 +251,3 @@ defmodule LSG.Telegram do
text: "Hey! You sent me a message: #{inspect update}")
end
end
-
-
diff --git a/lib/lsg_irc.ex b/lib/lsg_irc.ex
index c811d18..ba0828a 100644
--- a/lib/lsg_irc.ex
+++ b/lib/lsg_irc.ex
@@ -1,4 +1,6 @@
defmodule LSG.IRC do
+ require Logger
+
def application_childs do
env = Application.get_env(:lsg, :irc)
@@ -6,7 +8,9 @@ defmodule LSG.IRC do
IRC.Connection.setup()
IRC.Plugin.setup()
- for plugin <- Application.get_env(:lsg, :irc)[:plugins], do: IRC.Plugin.declare(plugin)
+
+ # Probably just needed for migration
+ #for plugin <- Application.get_env(:lsg, :irc)[:plugins], do: IRC.Plugin.declare(plugin)
[
worker(Registry, [[keys: :duplicate, name: IRC.ConnectionPubSub]], id: :registr_irc_conn),
@@ -22,8 +26,11 @@ defmodule LSG.IRC do
def after_start() do
# Start plugins first to let them get on connection events.
+ Logger.debug("IRC.after_start - initializing plugins")
IRC.Plugin.start_all()
+ Logger.debug("IRC.after_start - initializing connections")
IRC.Connection.start_all()
+ Logger.debug("IRC.after_start - ok")
end
end
diff --git a/lib/lsg_irc/alcolog_plugin.ex b/lib/lsg_irc/alcolog_plugin.ex
index 600dc1a..c758117 100644
--- a/lib/lsg_irc/alcolog_plugin.ex
+++ b/lib/lsg_irc/alcolog_plugin.ex
@@ -6,6 +6,7 @@ defmodule LSG.IRC.AlcoologPlugin do
* **!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.
+ * **!moar `[cl]` : enregistre un verre équivalent au dernier !santai.
* **-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.
@@ -139,7 +140,7 @@ defmodule LSG.IRC.AlcoologPlugin do
"attention... l'alcool permet de rendre l'eau potable",
"{{'QUOI ?' | bold}}",
"QUO{{'I' | rrepeat}}?",
- "{{'COMMENT ÇA DE L'EAU ?' | red}}",
+ "{{\"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 ?"
]
@@ -163,16 +164,20 @@ 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", [])
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:santai", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:moar", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:again", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:bis", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:santo", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:santeau", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:alcoolog", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:sobre", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:sobrepour", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:soif", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:alcoolisme", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:alcool", regopts)
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}])
@@ -182,8 +187,25 @@ defmodule LSG.IRC.AlcoologPlugin do
traverse_fun = fn(obj, dets) ->
case obj do
+ object = {nick, date, volumes, active, cl, deg, name, comment, meta} ->
+ :ets.insert(ets, {{nick, date}, volumes, active, cl, deg, name, comment, meta})
+ dets
object = {nick, date, volumes, active, name, comment} ->
- :ets.insert(ets, {{nick, date}, volumes, active, name, comment})
+ IO.puts("Migrating object #{inspect object}")
+ {cl, deg} = with \
+ %{"cl" => cl, "deg" => deg} <- Regex.named_captures(~r/^(?<cl>\d+[.]\d+)cl\s+(?<deg>\d+[.]\d+)°$/, name),
+ {cl, _} <- Util.float_paparse(cl),
+ {deg, _} <- Util.float_paparse(deg)
+ do
+ {cl, deg}
+ else
+ _ -> {nil, nil}
+ end
+ new = {nick, date, volumes, active, cl, deg, name, comment, Map.new()}
+ :dets.delete_object(dets, obj)
+ :dets.insert(dets, new)
+
+ :ets.insert(ets, {{nick, date}, volumes, active, cl, deg, name, comment, Map.new()})
dets
_ ->
dets
@@ -206,40 +228,40 @@ defmodule LSG.IRC.AlcoologPlugin do
|> 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
+ {txt, apero?} = cond do
now.hour >= 0 && now.hour < 6 ->
- ["apéro tardif ? Je dis OUI ! SANTAI !"]
+ {["apéro tardif ? Je dis OUI ! SANTAI !"], true}
now.hour >= 6 && now.hour < 12 ->
if day_of_week >= 6 do
- ["C'est quand même un peu tôt non ? Prochain apéro #{apero}"]
+ {["de l'alcool pour le petit dej ? le week-end, pas de problème !"], true}
else
- ["de l'alcool pour le petit dej ? le week-end, pas de problème !"]
+ {["C'est quand même un peu tôt non ? Prochain apéro #{apero}"], false}
end
now.hour >= 12 && (now.hour < 14) ->
- ["oui! c'est l'apéro de midi! (et apéro #{apero})",
+ {["oui! c'est l'apéro de midi! (et apéro #{apero})",
"tu peux attendre #{apero} ou y aller, il est midi !"
- ]
+ ], true}
now.hour == 17 ->
- [
+ {[
"ÇA APPROCHE !!! Apéro #{apero}",
"BIENTÔT !!! Apéro #{apero}",
"achetez vite les teilles, apéro dans #{apero}!",
"préparez les teilles, apéro dans #{apero}!"
- ]
+ ], false}
now.hour >= 14 && now.hour < 18 ->
weekend = if day_of_week >= 6 do
" ... ou maintenant en fait, c'est le week-end!"
else
""
end
- ["tiens bon! apéro #{apero}#{weekend}",
+ {["tiens bon! apéro #{apero}#{weekend}",
"courage... apéro dans #{apero}#{weekend}",
"pas encore :'( apéro dans #{apero}#{weekend}"
- ]
+ ], false}
true ->
- [
+ {[
"C'EST L'HEURE DE L'APÉRO !!! SANTAIIIIIIIIIIII !!!!"
- ]
+ ], true}
end
txt = txt
@@ -248,6 +270,11 @@ defmodule LSG.IRC.AlcoologPlugin do
m.replyfun.(txt)
+ stats = get_full_statistics(state, m.account.id)
+ if !apero? && stats.active > 0.1 do
+ m.replyfun.("(... ou continue en fait, je suis pas ta mère !)")
+ end
+
{:noreply, state}
end
@@ -344,15 +371,58 @@ defmodule LSG.IRC.AlcoologPlugin do
end
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, " ")
+ santai(m, state, cl, deg, comment)
+ {:noreply, state}
+ end
+
+ @moar [
+ "{{message.sender.nick}}: la même donc ?",
+ "{{message.sender.nick}}: et voilà la petite sœur !"
+ ]
+
+ def handle_info({:irc, :trigger, "bis", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do
+ handle_info({:irc, :trigger, "moar", m}, state)
+ end
+ def handle_info({:irc, :trigger, "again", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do
+ handle_info({:irc, :trigger, "moar", m}, state)
+ end
+
+ def handle_info({:irc, :trigger, "moar", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do
+ case get_statistics_for_nick(state, m.account.id) do
+ {_, obj = {_, _date, _points, _active, cl, deg, _name, comment, _meta}} ->
+ cl = case args do
+ [cls] ->
+ case Util.float_paparse(cls) do
+ {cl, _} -> cl
+ _ -> cl
+ end
+ _ -> cl
+ end
+ moar = @moar |> Enum.shuffle() |> Enum.random() |> Tmpl.render(m) |> m.replyfun.()
+ santai(m, state, cl, deg, comment, auto_set: true)
+ {_, obj = {_, date, points, _last_active, type, descr}} ->
+ case Regex.named_captures(~r/^(?<cl>\d+[.]\d+)cl\s+(?<deg>\d+[.]\d+)°$/, type) do
+ nil -> m.replyfun.("suce")
+ u ->
+ moar = @moar |> Enum.shuffle() |> Enum.random() |> Tmpl.render(m) |> m.replyfun.()
+ santai(m, state, u["cl"], u["deg"], descr, auto_set: true)
+ end
+ _ -> nil
+ end
+ {:noreply, state}
+ end
+
+ defp santai(m, state, cl, deg, comment, options \\ []) do
+ comment = cond do
+ comment == [] -> nil
+ is_binary(comment) -> comment
+ comment == nil -> nil
+ true -> Enum.join(comment, " ")
end
{cl, cl_extra} = case {Util.float_paparse(cl), cl} do
{{cl, extra}, _} -> {cl, extra}
- {:error, "("<>_} ->
+ {:error, "("<>_} ->
try do
{:ok, result} = Abacus.eval(cl)
{result, nil}
@@ -363,7 +433,7 @@ defmodule LSG.IRC.AlcoologPlugin do
end
{deg, comment, auto_set, beer_id} = case Util.float_paparse(deg) do
- {deg, _} -> {deg, comment, false, nil}
+ {deg, _} -> {deg, comment, Keyword.get(options, :auto_set, false), nil}
:error ->
beername = if(comment, do: "#{deg} #{comment}", else: deg)
case Untappd.search_beer(beername, limit: 1) do
@@ -374,7 +444,6 @@ defmodule LSG.IRC.AlcoologPlugin do
end
end
-
cond do
cl == nil -> m.replyfun.(cl_extra)
deg == nil -> m.replyfun.(comment)
@@ -383,16 +452,20 @@ defmodule LSG.IRC.AlcoologPlugin do
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)
+ now = m.at || DateTime.utc_now()
+ |> DateTime.to_unix(: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})
+ meta = %{}
+ meta = Map.put(meta, "timestamp", now)
+ meta = Map.put(meta, "weight", user_meta.weight)
+ meta = Map.put(meta, "sex", user_meta.sex)
+ :ok = :dets.insert(state.dets, {m.account.id, now, points, if(old_stats, do: old_stats.active, else: 0), cl, deg, name, comment, meta})
+ true = :ets.insert(state.ets, {{m.account.id, now}, points, if(old_stats, do: old_stats.active, else: 0),cl, deg, name, comment, meta})
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
+ k = if user_meta.sex, do: 0.7, else: 0.6
+ weight = user_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
@@ -418,12 +491,24 @@ defmodule LSG.IRC.AlcoologPlugin do
""
end
+ since_str = if stats.since && stats.since_min > 180 do
+ "(depuis: #{stats.since_s}) "
+ else
+ ""
+ end
+
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}) !"
+ "#{sante} #{nick} #{extra}#{up} #{format_points(points)} @#{stats.active}g/l [+#{peak} g/l]"
+ <> " (15m: #{stats.active15m}, 30m: #{stats.active30m}, 1h: #{stats.active1h}) #{since_str}(sobriété #{at} (dans #{stats.sober_in_s})#{sober_add}) !"
<> " (aujourd'hui #{stats.daily_volumes} points - #{stats.daily_gl} g/l)"
end
+ meta = if beer_id do
+ Map.put(meta, "untappd:beer_id", beer_id)
+ else
+ meta
+ end
+
if beer_id do
spawn(fn() ->
case Untappd.maybe_checkin(m.account, beer_id) do
@@ -434,7 +519,7 @@ defmodule LSG.IRC.AlcoologPlugin do
|> Enum.filter(fn(b) -> b end)
|> Enum.intersperse(", ")
|> Enum.join("")
- badge = if(badges > 1, do: "badges", else: "badge")
+ badge = if(length(badges) > 1, do: "badges", else: "badge")
m.replyfun.("\\O/ Unlocked untappd #{badge}: #{badges_s}")
end
:ok
@@ -445,7 +530,15 @@ defmodule LSG.IRC.AlcoologPlugin do
end)
end
- local_extra = if auto_set, do: " #{comment} (#{deg}°)", else: ""
+ local_extra = if auto_set do
+ if comment do
+ " #{comment} (#{cl}cl @ #{deg}°)"
+ else
+ "#{cl}cl @ #{deg}°"
+ end
+ else
+ ""
+ end
m.replyfun.(msg.(m.sender.nick, local_extra))
notify = IRC.Membership.notify_channels(m.account) -- [{m.network,m.channel}]
for {net, chan} <- notify do
@@ -456,6 +549,7 @@ defmodule LSG.IRC.AlcoologPlugin do
end
miss = cond do
+ points <= 0.6 -> :blague
stats.active30m >= 2.9 && stats.active30m < 3 -> :miss3
stats.active30m >= 1.9 && stats.active30m < 2 -> :miss2
stats.active30m >= 0.9 && stats.active30m < 1 -> :miss1
@@ -471,6 +565,9 @@ defmodule LSG.IRC.AlcoologPlugin do
end
miss = case miss do
+ :blague -> [
+ "c'est une blague ?!"
+ ]
:miss025 -> [
"si peu ?"
]
@@ -518,7 +615,6 @@ defmodule LSG.IRC.AlcoologPlugin do
end
end
- {:noreply, state}
end
def handle_info({:irc, :trigger, "santai", m = %IRC.Message{trigger: %IRC.Trigger{args: _, type: :bang}}}, state) do
@@ -549,13 +645,26 @@ defmodule LSG.IRC.AlcoologPlugin do
|> Enum.sort_by(fn({_, status}) -> status.active end, &>/2)
end
+ @spec since() :: %{IRC.Account.id() => DateTime.t()}
+ @doc "Returns the last time the user was at 0 g/l"
+ def since() do
+ :ets.foldr(fn({{acct, timestamp}, _vol, current, _cl, _deg, _name, _comment, _m}, acc) ->
+ if !Map.get(acc, acct) && current == 0 do
+ date = DateTime.from_unix!(timestamp, :millisecond)
+ Map.put(acc, acct, date)
+ else
+ acc
+ end
+ end, %{}, __MODULE__.ETS)
+ end
+
def get_full_statistics(nick) do
get_full_statistics(data_state(), nick)
end
defp get_full_statistics(state, nick) do
case get_statistics_for_nick(state, nick) do
- {count, {_, last_at, last_points, last_active, last_type, last_descr}} ->
+ {count, {_, last_at, last_points, last_active, last_cl, last_deg, last_type, last_descr, _meta}} ->
{active, active_drinks} = current_alcohol_level(state, nick)
{_, m30} = alcohol_level_rising(state, nick)
{rising, m15} = alcohol_level_rising(state, nick, 15)
@@ -601,20 +710,30 @@ defmodule LSG.IRC.AlcoologPlugin do
nil
end
+ since = if active > 0 do
+ since()
+ |> Map.get(nick)
+ end
+
+ since_diff = if since, do: Timex.diff(DateTime.utc_now(), since, :minutes)
+ since_duration = if since, do: Timex.Duration.from_minutes(since_diff)
+ since_s = if since, do: Timex.Format.Duration.Formatter.lformat(since_duration, "fr", :humanized)
+
user_status = list
|> Enum.shuffle()
|> Enum.random()
{total_volumes, total_gl} = user_stats(state, nick)
- %{active: active, last_at: last_at, last_points: last_points, last_type: last_type, last_descr: last_descr,
+ %{active: active, last_at: last_at, last_cl: last_cl, last_deg: last_deg, last_points: last_points, last_type: last_type, last_descr: last_descr,
trend_symbol: trend,
active5m: m5, active15m: m15, active30m: m30, active1h: h1,
rising: rising,
active_drinks: active_drinks,
user_status: user_status,
daily_gl: total_gl, daily_volumes: total_volumes,
- sober_in: minutes_til_sober, sober_in_s: sober_in_s
+ sober_in: minutes_til_sober, sober_in_s: sober_in_s,
+ since: since, since_min: since_diff, since_s: since_s,
}
_ ->
nil
@@ -660,7 +779,7 @@ defmodule LSG.IRC.AlcoologPlugin do
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)
+ stats = get_full_statistics(state, account.id)
if stats && stats.sober_in > 0 do
now = DateTime.utc_now()
sober = now |> DateTime.add(round(stats.sober_in*60), :second)
@@ -693,7 +812,12 @@ defmodule LSG.IRC.AlcoologPlugin do
else
status.trend_symbol
end
- "#{nick} #{status.user_status} #{trend_symbol} #{Float.round(status.active, 4)} g/l"
+ since_str = if status.since_min > 180 do
+ "depuis: #{status.since_s} | "
+ else
+ ""
+ end
+ "#{nick} #{status.user_status} #{trend_symbol} #{Float.round(status.active, 4)} g/l [#{since_str}sobre dans: #{status.sober_in_s}]"
end)
|> Enum.intersperse(", ")
|> Enum.join("")
@@ -732,54 +856,43 @@ defmodule LSG.IRC.AlcoologPlugin do
{: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)
- 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)
+ def user_over_time(account, count) do
+ user_over_time(data_state(), account, count)
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)
+ def user_over_time(state, account, count) do
+ delay = count*((24 * 60)*60)
now = DateTime.utc_now()
- before = now
- |> DateTime.add(-aday, :second)
+ before = DateTime.utc_now()
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
|> DateTime.to_unix(:millisecond)
- over_time_stats(before, 180, m, state)
- end
+ #[
+# {{{:"$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()
- 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)
+ Map.put(acc, date, Map.get(acc, date, 0) + vol)
+ end)
end
- defp user_over_time(state, account, count) do
+ def user_over_time_gl(account, count) do
+ state = data_state()
+ meta = get_user_meta(state, account.id)
delay = count*((24 * 60)*60)
now = DateTime.utc_now()
before = DateTime.utc_now()
@@ -787,14 +900,14 @@ defmodule LSG.IRC.AlcoologPlugin do
|> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
|> DateTime.to_unix(:millisecond)
#[
-# {{{:"$1", :"$2"}, :_, :_, :_, :_},
+# {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
# [{:andalso, {:==, :"$1", :"$1"}, {:<, :"$2", {:const, 3000}}}], [:lol]}
#]
- match = [{{{:"$1", :"$2"}, :_, :_, :_, :_},
+ match = [{{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
[{:andalso, {:>, :"$2", {:const, before}}, {:==, :"$1", {:const, account.id}}}], [:"$_"]}
]
:ets.select(state.ets, match)
- |> Enum.reduce(Map.new, fn({{_, ts}, vol, _, _, _}, acc) ->
+ |> Enum.reduce(Map.new, fn({{_, ts}, vol, _, _, _, _, _, _}, acc) ->
date = DateTime.from_unix!(ts, :millisecond)
|> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
@@ -804,23 +917,28 @@ defmodule LSG.IRC.AlcoologPlugin do
date
end
|> DateTime.to_date()
+ weight = meta.weight
+ k = if meta.sex, do: 0.7, else: 0.6
+ gl = (10*vol)/(k*weight)
- Map.put(acc, date, Map.get(acc, date, 0) + vol)
+ Map.put(acc, date, Map.get(acc, date, 0) + gl)
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"}, :_, :_, :_, :_},
+ #match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _, _, _, _}) when date > before -> obj end)
+ 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)
+ |> 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) ->
+ top = Enum.reduce(drinks, %{}, fn({{nick, _}, vol, _, _, _, _, _, _}, acc) ->
all = Map.get(acc, nick, 0)
Map.put(acc, nick, all + vol)
end)
@@ -872,7 +990,7 @@ defmodule LSG.IRC.AlcoologPlugin do
meta = Map.merge(@default_user_meta, %{sex: h, weight: weight, loss_factor: factor})
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!"]
+ thin = ["en route vers l'anorexie ?", "t'as vomi...", "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"
@@ -886,7 +1004,7 @@ defmodule LSG.IRC.AlcoologPlugin do
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.account.id) do
- {_, obj = {_, date, points, _last_active, type, descr}} ->
+ {_, obj = {_, date, points, _last_active, _cl, _deg, type, descr, _meta}} ->
:dets.delete_object(state.dets, obj)
:ets.delete(state.ets, {m.account.id, date})
m.replyfun.("supprimé: #{m.sender.nick} #{points} #{type} #{descr}")
@@ -903,6 +1021,7 @@ defmodule LSG.IRC.AlcoologPlugin do
end
end
+
def handle_info({:irc, :trigger, "alcoolisme", m = %IRC.Message{trigger: %IRC.Trigger{args: args, type: :bang}}}, state) do
{account, duration} = case args do
[nick | rest] -> {IRC.Account.find_always_by_nick(m.network, m.channel, nick), rest}
@@ -951,6 +1070,7 @@ defmodule LSG.IRC.AlcoologPlugin do
<> (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: "")
+ <> (if stats.since && stats.since_min > 180, do: "— Paitai depuis #{stats.since_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: "")
@@ -969,7 +1089,7 @@ defmodule LSG.IRC.AlcoologPlugin do
# Account merge
def handle_info({:account_change, old_id, new_id}, state) do
- spec = [{{:"$1", :_, :_, :_, :_, :_}, [{:==, :"$1", {:const, old_id}}], [:"$_"]}]
+ 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)
@@ -991,11 +1111,11 @@ defmodule LSG.IRC.AlcoologPlugin do
end
end
- defp rename_object_owner(table, ets, object = {old_id, date, volume, current, name, comment}, old_id, new_id) do
+ defp rename_object_owner(table, ets, object = {old_id, date, volume, current, cl, deg, name, comment, meta}, 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})
+ :dets.insert(table, {new_id, date, volume, current, cl, deg, name, comment, meta})
+ :ets.insert(ets, {{new_id, date}, volume, current, cl, deg, name, comment, meta})
end
# Account: move from nick to account id
@@ -1005,9 +1125,9 @@ defmodule LSG.IRC.AlcoologPlugin do
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", :_, :_, :_, :_, :_}, [], [:"$_"]}]
+ 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}) ->
+ Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj = {nick, _date, _vol, _cur, _cl, _deg, _name, _comment, _meta}) ->
#Logger.debug("accounts:: item #{inspect(obj)}")
if new_id = Map.get(mapping, nick) do
Logger.debug("alcolog/accounts:: merging #{nick} -> #{new_id}")
@@ -1019,7 +1139,7 @@ defmodule LSG.IRC.AlcoologPlugin do
def handle_info({:account, _net, _chan, nick, account_id}, state) do
nick = String.downcase(nick)
- spec = [{{:"$1", :_, :_, :_, :_, :_}, [{:==, :"$1", {:const, 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)
@@ -1039,10 +1159,19 @@ defmodule LSG.IRC.AlcoologPlugin do
{:noreply, state}
end
+ def nick_history(account) do
+ spec = [
+ {{{:"$1", :_}, :_, :_, :_, :_, :_, :_, :_},
+ [{:==, :"$1", {:const, account.id}}],
+ [:"$_"]}
+ ]
+ :ets.select(data_state().ets, spec)
+ end
+
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)
+ |> Enum.sort_by(fn({_, ts, _, _, _, _, _, _, _}) -> ts end, &</2)
+ count = Enum.reduce(qvc, 0, fn({_nick, _ts, points, _active, _cl, _deg, _type, _descr, _meta}, acc) -> acc + (points||0) end)
last = List.last(qvc) || nil
{count, last}
end
@@ -1102,6 +1231,10 @@ defmodule LSG.IRC.AlcoologPlugin do
# stop folding when ?
#
+ def user_stats(account) do
+ user_stats(data_state(), account.id)
+ end
+
defp user_stats(state = %{ets: ets}, account_id) do
meta = get_user_meta(state, account_id)
aday = (10 * 60)*60
@@ -1111,7 +1244,7 @@ defmodule LSG.IRC.AlcoologPlugin do
|> DateTime.to_unix(:millisecond)
#match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end)
match = [
- {{{:"$1", :"$2"}, :_, :_, :_, :_},
+ {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
[
{:>, :"$2", {:const, before}},
{:"=:=", {:const, account_id}, :"$1"}
@@ -1120,7 +1253,7 @@ defmodule LSG.IRC.AlcoologPlugin do
# tuple ets: {{nick, date}, volumes, current, nom, commentaire}
drinks = :ets.select(ets, match)
# {date, single_peak}
- total_volume = Enum.reduce(drinks, 0.0, fn({{_, date}, volume, _, _, _}, acc) ->
+ total_volume = Enum.reduce(drinks, 0.0, fn({{_, date}, volume, _, _, _, _, _, _}, acc) ->
acc + volume
end)
k = if meta.sex, do: 0.7, else: 0.6
@@ -1155,7 +1288,7 @@ defmodule LSG.IRC.AlcoologPlugin do
|> DateTime.to_unix(:millisecond)
#match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end)
match = [
- {{{:"$1", :"$2"}, :_, :_, :_, :_},
+ {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
[
{:>, :"$2", {:const, before}},
{:"=:=", {:const, account_id}, :"$1"}
@@ -1163,9 +1296,9 @@ defmodule LSG.IRC.AlcoologPlugin do
]
# tuple ets: {{nick, date}, volumes, current, nom, commentaire}
drinks = :ets.select(ets, match)
- |> Enum.sort_by(fn({{_, date}, _, _, _, _}) -> date end, &</2)
+ |> Enum.sort_by(fn({{_, date}, _, _, _, _, _, _, _}) -> date end, &</2)
# {date, single_peak}
- {all, last_drink_at, gl, active_drinks} = Enum.reduce(drinks, {0.0, nil, [], 0}, fn({{_, date}, volume, _, _, _}, {all, last_at, acc, active_drinks}) ->
+ {all, last_drink_at, gl, active_drinks} = Enum.reduce(drinks, {0.0, nil, [], 0}, fn({{_, date}, volume, _, _, _, _, _, _}, {all, last_at, acc, active_drinks}) ->
k = if meta.sex, do: 0.7, else: 0.6
weight = meta.weight
peak = (10*volume)/(k*weight)
@@ -1249,4 +1382,3 @@ defmodule LSG.IRC.AlcoologPlugin do
end
end
-
diff --git a/lib/lsg_irc/alcoolog_announcer_plugin.ex b/lib/lsg_irc/alcoolog_announcer_plugin.ex
index 28973ca..3902d5f 100644
--- a/lib/lsg_irc/alcoolog_announcer_plugin.ex
+++ b/lib/lsg_irc/alcoolog_announcer_plugin.ex
@@ -24,7 +24,26 @@ defmodule LSG.IRC.AlcoologAnnouncerPlugin do
def irc_doc, do: nil
- def start_link(), do: GenServer.start_link(__MODULE__, [])
+ def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
+
+ def log(account) do
+ dets_filename = (LSG.data_path() <> "/" <> "alcoologlog.dets") |> String.to_charlist
+ {:ok, dets} = :dets.open_file(dets_filename, [{:type,:bag}])
+ from = ~U[2020-08-23 19:41:40.524154Z]
+ to = ~U[2020-08-24 19:41:40.524154Z]
+ select = [
+ {{:"$1", :"$2", :_},
+ [
+ {:andalso,
+ {:andalso, {:==, :"$1", {:const, account.id}},
+ {:>, :"$2", {:const, DateTime.to_unix(from)}}},
+ {:<, :"$2", {:const, DateTime.to_unix(to)}}}
+ ], [:"$_"]}
+ ]
+ res = :dets.select(dets, select)
+ :dets.close(dets)
+ res
+ end
def init(_) do
{:ok, _} = Registry.register(IRC.PubSub, "account", [])
@@ -32,21 +51,42 @@ defmodule LSG.IRC.AlcoologAnnouncerPlugin do
Process.send_after(self(), :stats, :timer.seconds(30))
dets_filename = (LSG.data_path() <> "/" <> "alcoologlog.dets") |> String.to_charlist
{:ok, dets} = :dets.open_file(dets_filename, [{:type,:bag}])
+ ets = nil # :ets.new(__MODULE__.ETS, [:ordered_set, :named_table, :protected, {:read_concurrency, true}])
#:ok = LSG.IRC.SettingPlugin.declare("alcoolog.alerts", __MODULE__, true, :boolean)
#:ok = LSG.IRC.SettingPlugin.declare("alcoolog.aperoalert", __MODULE__, true, :boolean)
- {:ok, {stats, now(), dets}}
+ #
+ {:ok, {stats, now(), dets, ets}}#, {:continue, :traverse}}
+ end
+
+ def handle_continue(:traverse, state = {_, _, dets, ets}) do
+ traverse_fun = fn(obj, dets) ->
+ case obj do
+ {nick, %DateTime{} = dt, active} ->
+ :dets.delete_object(dets, obj)
+ :dets.insert(dets, {nick, DateTime.to_unix(dt), active})
+ IO.puts("ok #{inspect obj}")
+ dets
+ {nick, ts, value} ->
+ :ets.insert(ets, { {nick, ts}, value })
+ dets
+ end
+ end
+ :dets.foldl(traverse_fun, dets, dets)
+ :dets.sync(dets)
+ IO.puts("alcoolog announcer fixed")
+ {:noreply, state}
end
def alcohol_reached(old, new, level) do
(old.active < level && new.active >= level) && (new.active5m >= level)
end
-
+
def alcohol_below(old, new, level) do
(old.active > level && new.active <= level) && (new.active5m <= level)
end
- def handle_info(:stats, {old_stats, old_now, dets}) do
+ def handle_info(:stats, {old_stats, old_now, dets, ets}) do
stats = get_stats()
now = now()
@@ -73,10 +113,13 @@ defmodule LSG.IRC.AlcoologAnnouncerPlugin do
new = Map.get(stats, acct, nil)
#IO.puts "#{acct}: #{inspect(old)} -> #{inspect(new)}"
+ now = DateTime.to_unix(DateTime.utc_now())
if new && new[:active] do
- :dets.insert(dets, {acct, DateTime.utc_now(), new[:active]})
+ :dets.insert(dets, {acct, now, new[:active]})
+ :ets.insert(ets, {{acct, now}, new[:active]})
else
- :dets.insert(dets, {acct, DateTime.utc_now(), 0.0})
+ :dets.insert(dets, {acct, now, 0.0})
+ :ets.insert(ets, {{acct, now}, new[:active]})
end
event = cond do
@@ -206,7 +249,7 @@ defmodule LSG.IRC.AlcoologAnnouncerPlugin do
timer()
#IO.puts "tick stats ok"
- {:noreply, {stats,now,dets}}
+ {:noreply, {stats,now,dets,ets}}
end
def handle_info(_, state) do
diff --git a/lib/lsg_irc/base_plugin.ex b/lib/lsg_irc/base_plugin.ex
index 69b02e8..a95f45c 100644
--- a/lib/lsg_irc/base_plugin.ex
+++ b/lib/lsg_irc/base_plugin.ex
@@ -3,14 +3,15 @@ defmodule LSG.IRC.BasePlugin do
def irc_doc, do: nil
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:version", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:help", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:liquidrender", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:plugin", [])
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:version", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:help", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:liquidrender", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:plugin", regopts)
{:ok, nil}
end
@@ -105,4 +106,3 @@ defmodule LSG.IRC.BasePlugin do
end
end
-
diff --git a/lib/lsg_irc/bourosama_plugin.ex b/lib/lsg_irc/bourosama_plugin.ex
index 7dde662..ba63d81 100644
--- a/lib/lsg_irc/bourosama_plugin.ex
+++ b/lib/lsg_irc/bourosama_plugin.ex
@@ -13,14 +13,15 @@ defmodule LSG.IRC.BoursoramaPlugin do
end
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@cac40_url "https://www.boursorama.com/bourse/actions/palmares/france/?france_filter%5Bmarket%5D=1rPCAC&france_filter%5Bsector%5D=&france_filter%5Bvariation%5D=50002&france_filter%5Bperiod%5D=1&france_filter%5Bfilter%5D="
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:cac40", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:caca40", [])
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:cac40", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:caca40", regopts)
{:ok, nil}
end
diff --git a/lib/lsg_irc/calc_plugin.ex b/lib/lsg_irc/calc_plugin.ex
index b8eee39..ca65675 100644
--- a/lib/lsg_irc/calc_plugin.ex
+++ b/lib/lsg_irc/calc_plugin.ex
@@ -8,11 +8,11 @@ defmodule LSG.IRC.CalcPlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:calc", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:calc", [plugin: __MODULE__])
{:ok, nil}
end
@@ -35,4 +35,3 @@ defmodule LSG.IRC.CalcPlugin do
end
end
-
diff --git a/lib/lsg_irc/coronavirus_plugin.ex b/lib/lsg_irc/coronavirus_plugin.ex
index 8038d14..b9a9e40 100644
--- a/lib/lsg_irc/coronavirus_plugin.ex
+++ b/lib/lsg_irc/coronavirus_plugin.ex
@@ -14,15 +14,22 @@ defmodule LSG.IRC.CoronavirusPlugin do
"""
def irc_doc, do: @moduledoc
- def start_link(), do: GenServer.start_link(__MODULE__, [])
+ def start_link() do
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
+ end
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:coronavirus", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:coronavirus", [plugin: __MODULE__])
+ {:ok, nil, {:continue, :init}}
+ :ignore
+ end
+
+ def handle_continue(:init, _) do
date = Date.add(Date.utc_today(), -2)
{data, _} = fetch_data(%{}, date)
{data, next} = fetch_data(data)
:timer.send_after(next, :update)
- {:ok, %{data: data}}
+ {:noreply, %{data: data}}
end
def handle_info(:update, state) do
@@ -163,4 +170,3 @@ defmodule LSG.IRC.CoronavirusPlugin do
end
end
-
diff --git a/lib/lsg_irc/correction_plugin.ex b/lib/lsg_irc/correction_plugin.ex
index e7b2577..f370cf8 100644
--- a/lib/lsg_irc/correction_plugin.ex
+++ b/lib/lsg_irc/correction_plugin.ex
@@ -7,12 +7,12 @@ defmodule LSG.IRC.CorrectionPlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "message", [])
- {:ok, _} = Registry.register(IRC.PubSub, "triggers", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "message", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(IRC.PubSub, "triggers", [plugin: __MODULE__])
{:ok, %{}}
end
@@ -25,7 +25,7 @@ defmodule LSG.IRC.CorrectionPlugin do
{:noreply, correction(m, state)}
end
- defp correction(m, state) do
+ def correction(m, state) do
history = Map.get(state, key(m), [])
if String.starts_with?(m.text, "s/") do
case String.split(m.text, "/") do
diff --git a/lib/lsg_irc/dice_plugin.ex b/lib/lsg_irc/dice_plugin.ex
index a507b8e..eafd88a 100644
--- a/lib/lsg_irc/dice_plugin.ex
+++ b/lib/lsg_irc/dice_plugin.ex
@@ -17,11 +17,11 @@ defmodule LSG.IRC.DicePlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:dice", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:dice", [plugin: __MODULE__])
{:ok, %__MODULE__{}}
end
diff --git a/lib/lsg_irc/finance_plugin.ex b/lib/lsg_irc/finance_plugin.ex
index 7266a5e..51ec3f6 100644
--- a/lib/lsg_irc/finance_plugin.ex
+++ b/lib/lsg_irc/finance_plugin.ex
@@ -23,8 +23,8 @@ defmodule LSG.IRC.FinancePlugin do
"""
- @currency_list "https://www.alphavantage.co/physical_currency_list/"
- @crypto_list "https://www.alphavantage.co/digital_currency_list/"
+ @currency_list "http://www.alphavantage.co/physical_currency_list/"
+ @crypto_list "http://www.alphavantage.co/digital_currency_list/"
HTTPoison.start()
load_currency = fn(url) ->
@@ -47,13 +47,14 @@ defmodule LSG.IRC.FinancePlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:forex", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:currency", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:stocks", [])
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:forex", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:currency", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:stocks", regopts)
{:ok, nil}
end
diff --git a/lib/lsg_irc/kick_roulette_plugin.ex b/lib/lsg_irc/kick_roulette_plugin.ex
index 83efcb0..f810a74 100644
--- a/lib/lsg_irc/kick_roulette_plugin.ex
+++ b/lib/lsg_irc/kick_roulette_plugin.ex
@@ -7,11 +7,11 @@ defmodule LSG.IRC.KickRoulettePlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:kick", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:kick", [plugin: __MODULE__])
{:ok, nil}
end
diff --git a/lib/lsg_irc/last_fm_plugin.ex b/lib/lsg_irc/last_fm_plugin.ex
index 067a44e..8497f2d 100644
--- a/lib/lsg_irc/last_fm_plugin.ex
+++ b/lib/lsg_irc/last_fm_plugin.ex
@@ -14,13 +14,14 @@ defmodule LSG.IRC.LastFmPlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "account", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:lastfm", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:lastfmall", [])
+ regopts = [type: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:lastfm", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:lastfmall", regopts)
dets_filename = (LSG.data_path() <> "/" <> "lastfm.dets") |> String.to_charlist
{:ok, dets} = :dets.open_file(dets_filename, [])
{:ok, %__MODULE__{dets: dets}}
diff --git a/lib/lsg_irc/link_plugin.ex b/lib/lsg_irc/link_plugin.ex
index ea6df0c..ced80b2 100644
--- a/lib/lsg_irc/link_plugin.ex
+++ b/lib/lsg_irc/link_plugin.ex
@@ -52,8 +52,8 @@ defmodule LSG.IRC.LinkPlugin do
defstruct [:client]
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "message", [])
- #{:ok, _} = Registry.register(IRC.PubSub, "message:telegram", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "message", [plugin: __MODULE__])
+ #{:ok, _} = Registry.register(IRC.PubSub, "message:telegram", [plugin: __MODULE__])
Logger.info("Link handler started")
{:ok, %__MODULE__{}}
end
@@ -159,7 +159,7 @@ defmodule LSG.IRC.LinkPlugin do
{length, _} = Integer.parse(length)
handlers = Keyword.get(Application.get_env(:lsg, __MODULE__, [handlers: []]), :handlers)
- handler = Enum.reduce_while(handlers, nil, fn({module, opts}, acc) ->
+ handler = Enum.reduce_while(handlers, false, fn({module, opts}, acc) ->
module = Module.concat([module])
try do
case module.post_match(url, content_type, headers, opts) do
@@ -225,8 +225,9 @@ defmodule LSG.IRC.LinkPlugin do
end
end
- defp get_body(_, len, client, _, _acc) do
+ defp get_body(_, len, client, h, _acc) do
:hackney.close(client)
+ IO.inspect(h)
{:ok, "Error: file over 30"}
end
@@ -243,6 +244,8 @@ defmodule LSG.IRC.LinkPlugin do
{:error, status, _headers} ->
text = Plug.Conn.Status.reason_phrase(status)
{:ok, acc, "Error: HTTP #{text} (#{status})"}
+ {:error, {:tls_alert, {:handshake_failure, err}}} ->
+ {:ok, acc, "TLS Error: #{to_string(err)}"}
{:error, reason} ->
{:ok, acc, "Error: #{to_string(reason)}"}
end
diff --git a/lib/lsg_irc/np_handler.ex b/lib/lsg_irc/np_handler.ex
index 5f724d4..1a52c75 100644
--- a/lib/lsg_irc/np_handler.ex
+++ b/lib/lsg_irc/np_handler.ex
@@ -8,12 +8,12 @@ defmodule LSG.IRC.NpHandler do
def short_irc_doc, do: "!np (en ce moment sur 115ans)"
def irc_doc, do: @moduledoc
def start_link(client) do
- GenServer.start_link(__MODULE__, [client])
+ GenServer.start_link(__MODULE__, [client], name: __MODULE__)
end
def init([client]) do
ExIRC.Client.add_handler client, self
- {:ok, _} = Registry.register(LSG.BroadcastRegistry, "icecast", [])
+ {:ok, _} = Registry.register(LSG.BroadcastRegistry, "icecast", [plugin: __MODULE__])
{:ok, client}
end
diff --git a/lib/lsg_irc/outline_plugin.ex b/lib/lsg_irc/outline_plugin.ex
index 7bfaac1..471448b 100644
--- a/lib/lsg_irc/outline_plugin.ex
+++ b/lib/lsg_irc/outline_plugin.ex
@@ -12,14 +12,15 @@ defmodule LSG.IRC.OutlinePlugin do
require Logger
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
defstruct [:file, :hosts]
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:outline", [])
- {:ok, _} = Registry.register(IRC.PubSub, "message", [])
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:outline", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "message", regopts)
file = Path.join(LSG.data_path, "/outline.txt")
hosts = case File.read(file) do
{:error, :enoent} ->
@@ -51,7 +52,8 @@ defmodule LSG.IRC.OutlinePlugin do
uri = URI.parse(word)
if uri.scheme && uri.host do
if Enum.any?(state.hosts, fn(host) -> String.ends_with?(uri.host, host) end) do
- line = "-> https://outline.com/#{word}"
+ outline_url = outline(word)
+ line = "-> #{outline(word)}"
message.replyfun.(line)
end
end
@@ -69,4 +71,32 @@ defmodule LSG.IRC.OutlinePlugin do
File.write(file, string)
end
+ def outline(url) do
+ unexpanded = "https://outline.com/#{url}"
+ headers = [
+ {"User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0"},
+ {"Accept", "*/*"},
+ {"Accept-Language", "en-US,en;q=0.5"},
+ {"Origin", "https://outline.com"},
+ {"DNT", "1"},
+ {"Referer", unexpanded},
+ {"Pragma", "no-cache"},
+ {"Cache-Control", "no-cache"}
+ ]
+ params = %{"source_url" => url}
+ case HTTPoison.get("https://api.outline.com/v3/parse_article", headers, params: params) do
+ {:ok, %HTTPoison.Response{status_code: 200, body: json}} ->
+ body = Poison.decode!(json)
+ if Map.get(body, "success") do
+ code = get_in(body, ["data", "short_code"])
+ "https://outline.com/#{code}"
+ else
+ unexpanded
+ end
+ error ->
+ Logger.info("outline.com error: #{inspect error}")
+ unexpanded
+ end
+ end
+
end
diff --git a/lib/lsg_irc/preums_plugin.ex b/lib/lsg_irc/preums_plugin.ex
index 1f9a76b..7bb2c78 100644
--- a/lib/lsg_irc/preums_plugin.ex
+++ b/lib/lsg_irc/preums_plugin.ex
@@ -6,6 +6,35 @@ defmodule LSG.IRC.PreumsPlugin do
* `.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]
@@ -17,9 +46,9 @@ defmodule LSG.IRC.PreumsPlugin do
end
def all(dets, channel) do
- fun = fn({{chan, date}, nick, time, perfect, text}, acc) ->
+ fun = fn({{chan, date}, account_id, time, perfect, text}, acc) ->
if channel == chan do
- [%{date: date, nick: nick, time: time, perfect: perfect, text: text} | acc]
+ [%{date: date, account_id: account_id, time: time, perfect: perfect, text: text} | acc]
else
acc
end
@@ -27,22 +56,28 @@ defmodule LSG.IRC.PreumsPlugin do
:dets.foldl(fun, [], dets)
end
- def topnicks(dets, channel) do
- fun = fn(x = {{chan, date}, nick, _time, _perfect, _text}, acc) ->
+ 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 = Map.get(acc, nick, 0)
- Map.put(acc, nick, count + 1)
+ {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({nick, count}) -> count end, &>=/2)
+ |> 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__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def dets do
@@ -50,9 +85,10 @@ defmodule LSG.IRC.PreumsPlugin do
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "account", [])
- {:ok, _} = Registry.register(IRC.PubSub, "message", [])
- {:ok, _} = Registry.register(IRC.PubSub, "triggers", [])
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "message", 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
@@ -98,14 +134,17 @@ defmodule LSG.IRC.PreumsPlugin do
i
else
case :dets.lookup(state.dets, key) do
- [item = {^key, _nick, _now, _perfect, _text}] -> item
+ [item = {^key, _account_id, _now, _perfect, _text}] -> item
_ -> nil
end
end
if item do
- {_, nick, date, _perfect, text} = item
+ {_, 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}
@@ -115,11 +154,13 @@ defmodule LSG.IRC.PreumsPlugin do
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)
- |> Enum.map(fn({nick, count}) ->
- "#{nick} (#{count})"
+ 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.filter(fn(x) -> x end)
|> Enum.intersperse(", ")
|> Enum.join("")
msg = unless top == "" do
@@ -227,4 +268,9 @@ defmodule LSG.IRC.PreumsPlugin do
end
end
+ def score(_chan, _account, _time, _perfect, _text) do
+ 1
+ end
+
+
end
diff --git a/lib/lsg_irc/quatre_cent_vingt_plugin.ex b/lib/lsg_irc/quatre_cent_vingt_plugin.ex
index db85d49..fff7e4f 100644
--- a/lib/lsg_irc/quatre_cent_vingt_plugin.ex
+++ b/lib/lsg_irc/quatre_cent_vingt_plugin.ex
@@ -30,16 +30,17 @@ defmodule LSG.IRC.QuatreCentVingtPlugin do
def irc_doc, do: @moduledoc
- def start_link, do: GenServer.start_link(__MODULE__, [])
+ def start_link, do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
def init(_) do
for coeff <- @coeffs do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:#{420*coeff}", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:#{420*coeff}", [plugin: __MODULE__])
end
- {:ok, _} = Registry.register(IRC.PubSub, "account", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "account", [plugin: __MODULE__])
dets_filename = (LSG.data_path() <> "/420.dets") |> String.to_charlist
{:ok, dets} = :dets.open_file(dets_filename, [{:type,:bag},{:repair,:force}])
{:ok, dets}
+ :ignore
end
for coeff <- @coeffs do
diff --git a/lib/lsg_irc/say_plugin.ex b/lib/lsg_irc/say_plugin.ex
index 6a4f547..690d0a6 100644
--- a/lib/lsg_irc/say_plugin.ex
+++ b/lib/lsg_irc/say_plugin.ex
@@ -14,13 +14,14 @@ defmodule LSG.IRC.SayPlugin do
end
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:say", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:asay", [])
- {:ok, _} = Registry.register(IRC.PubSub, "message:private", [])
+ regopts = [type: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:say", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:asay", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "message:private", regopts)
{:ok, nil}
end
diff --git a/lib/lsg_irc/script_plugin.ex b/lib/lsg_irc/script_plugin.ex
index 28ae2a7..bae6f3f 100644
--- a/lib/lsg_irc/script_plugin.ex
+++ b/lib/lsg_irc/script_plugin.ex
@@ -19,11 +19,11 @@ defmodule LSG.IRC.ScriptPlugin do
def irc_doc, do: @ircdoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:script", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:script", [plugin: __MODULE__])
dets_filename = (LSG.data_path() <> "/" <> "scripts.dets") |> String.to_charlist
{:ok, dets} = :dets.open_file(dets_filename, [])
{:ok, %{dets: dets}}
@@ -40,4 +40,3 @@ defmodule LSG.IRC.ScriptPlugin do
end
end
-
diff --git a/lib/lsg_irc/seen_plugin.ex b/lib/lsg_irc/seen_plugin.ex
index 8b2178f..f1a5473 100644
--- a/lib/lsg_irc/seen_plugin.ex
+++ b/lib/lsg_irc/seen_plugin.ex
@@ -7,12 +7,13 @@ defmodule LSG.IRC.SeenPlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "triggers", [])
- {:ok, _} = Registry.register(IRC.PubSub, "message", [])
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "triggers", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "message", regopts)
dets_filename = (LSG.data_path() <> "/seen.dets") |> String.to_charlist()
{:ok, dets} = :dets.open_file(dets_filename, [])
{:ok, %{dets: dets}}
diff --git a/lib/lsg_irc/sms_plugin.ex b/lib/lsg_irc/sms_plugin.ex
index 60554fb..a37fb4e 100644
--- a/lib/lsg_irc/sms_plugin.ex
+++ b/lib/lsg_irc/sms_plugin.ex
@@ -54,7 +54,7 @@ defmodule LSG.IRC.SmsPlugin do
end
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def path() do
@@ -88,9 +88,10 @@ defmodule LSG.IRC.SmsPlugin do
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:sms", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:sms", [plugin: __MODULE__])
:ok = register_ovh_callback()
{:ok, %{}}
+ :ignore
end
def handle_info({:irc, :trigger, "sms", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang, args: [nick | text]}}}, state) do
diff --git a/lib/lsg_irc/tell_plugin.ex b/lib/lsg_irc/tell_plugin.ex
new file mode 100644
index 0000000..a683b43
--- /dev/null
+++ b/lib/lsg_irc/tell_plugin.ex
@@ -0,0 +1,93 @@
+defmodule LSG.IRC.TellPlugin do
+ use GenServer
+
+ @moduledoc """
+ # Tell
+
+ * **!tell `<nick>` `<message>`**: tell `message` to `nick` when they reconnect.
+ """
+
+ def irc_doc, do: @moduledoc
+ def start_link() do
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
+ end
+
+ def dets do
+ (LSG.data_path() <> "/tell.dets") |> String.to_charlist()
+ end
+
+ def init([]) do
+ regopts = [plugin: __MODULE__]
+ {:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:tell", regopts)
+ {:ok, dets} = :dets.open_file(dets(), [type: :bag])
+ {:ok, %{dets: dets}}
+ end
+
+ def handle_info({:irc, :trigger, "tell", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang, args: [nick_target | message]}}}, state) do
+ target = IRC.Account.find_always_by_nick(m.network, m.channel, nick_target)
+ message = Enum.join(message, " ")
+ with \
+ {:target, %IRC.Account{} = target} <- {:target, target},
+ {:same, false} <- {:same, target.id == m.account.id},
+ target_user = IRC.UserTrack.find_by_account(m.network, target),
+ target_nick = if(target_user, do: target_user.nick, else: target.name),
+ present? = if(target_user, do: Map.has_key?(target_user.last_active, m.channel)),
+ {:absent, true, _} <- {:absent, !present?, target_nick},
+ {:message, message} <- {:message, message}
+ do
+ obj = { {m.network, m.channel, target.id}, m.account.id, message, NaiveDateTime.utc_now()}
+ :dets.insert(state.dets, obj)
+ m.replyfun.("will tell to #{target_nick}")
+ else
+ {:same, _} -> m.replyfun.("are you so stupid that you need a bot to tell yourself things ?")
+ {:target, _} -> m.replyfun.("#{nick_target} unknown")
+ {:absent, _, nick} -> m.replyfun.("#{nick} is here, tell yourself!")
+ {:message, _} -> m.replyfun.("can't tell without a message")
+ end
+ {:noreply, state}
+ end
+
+ def handle_info({:account, network, channel, nick, account_id}, state) do
+ messages = :dets.lookup(state.dets, {network, channel, account_id})
+ if messages != [] do
+ strs = Enum.map(messages, fn({_, from, message, at}) ->
+ account = IRC.Account.get(from)
+ user = IRC.UserTrack.find_by_account(network, account)
+ fromnick = if user, do: user.nick, else: account.name
+ "#{nick}: <#{fromnick}> #{message}"
+ end)
+ Enum.each(strs, fn(s) -> IRC.Connection.broadcast_message(network, channel, s) end)
+ :dets.delete(state.dets, {network, channel, account_id})
+ end
+ {:noreply, state}
+ end
+
+ def handle_info({:account_change, old_id, new_id}, state) do
+ #:ets.fun2ms(fn({ {_net, _chan, target_id}, from_id, _, _} = obj) when (target_id == old_id) or (from_id == old_id) -> obj end)
+ spec = [{{{:"$1", :"$2", :"$3"}, :"$4", :_, :_}, [{:orelse, {:==, :"$3", {:const, old_id}}, {:==, :"$4", {:const, old_id}}}], [:"$_"]}]
+ Util.Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
+ case obj do
+ { {net, chan, ^old_id}, from_id, message, at } = obj ->
+ :dets.delete(obj)
+ :dets.insert(table, {{net, chan, new_id}, from_id, message, at})
+ {key, ^old_id, message, at} = obj ->
+ :dets.delete(table, obj)
+ :dets.insert(table, {key, new_id, message, at})
+ _ -> :ok
+ end
+ end)
+ {:noreply, state}
+ end
+
+
+ def handle_info(info, state) do
+ {:noreply, state}
+ end
+
+ def terminate(_, state) do
+ :dets.close(state.dets)
+ :ok
+ end
+
+end
diff --git a/lib/lsg_irc/txt_plugin.ex b/lib/lsg_irc/txt_plugin.ex
index f8c3a29..0f97bcc 100644
--- a/lib/lsg_irc/txt_plugin.ex
+++ b/lib/lsg_irc/txt_plugin.ex
@@ -33,7 +33,7 @@ defmodule LSG.IRC.TxtPlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
defstruct triggers: %{}, rw: true, locks: nil, markov_handler: nil, markov: nil
@@ -43,7 +43,7 @@ defmodule LSG.IRC.TxtPlugin do
{:ok, locks} = :dets.open_file(dets_locks_filename, [])
markov_handler = Keyword.get(Application.get_env(:lsg, __MODULE__, []), :markov_handler, LSG.IRC.TxtPlugin.Markov.Native)
{:ok, markov} = markov_handler.start_link()
- {:ok, _} = Registry.register(IRC.PubSub, "triggers", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "triggers", [plugin: __MODULE__])
{:ok, %__MODULE__{locks: locks, markov_handler: markov_handler, markov: markov, triggers: load()}}
end
@@ -147,13 +147,21 @@ defmodule LSG.IRC.TxtPlugin do
def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :bang, args: args}}}, state) do
grep = Enum.join(args, " ")
- result = with_stateful_results(msg, {:bang,"txt",grep}, fn() ->
+ |> String.downcase
+ |> :unicode.characters_to_nfd_binary()
+
+ result = with_stateful_results(msg, {:bang,"txt",msg.network,msg.channel,grep}, fn() ->
Enum.reduce(state.triggers, [], fn({trigger, data}, acc) ->
Enum.reduce(data, acc, fn({l, _}, acc) ->
[{trigger, l} | acc]
end)
end)
- |> Enum.filter(fn({_, line}) -> String.contains?(String.downcase(line), String.downcase(grep)) end)
+ |> Enum.filter(fn({_, line}) ->
+ line
+ |> String.downcase()
+ |> :unicode.characters_to_nfd_binary()
+ |> String.contains?(grep)
+ end)
|> Enum.shuffle()
end)
diff --git a/lib/lsg_irc/untappd_plugin.ex b/lib/lsg_irc/untappd_plugin.ex
index 02b22e3..69e4be6 100644
--- a/lib/lsg_irc/untappd_plugin.ex
+++ b/lib/lsg_irc/untappd_plugin.ex
@@ -18,7 +18,7 @@ defmodule LSG.IRC.UntappdPlugin do
end
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:beer", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:beer", [plugin: __MODULE__])
{:ok, %{}}
end
diff --git a/lib/lsg_irc/wikipedia_plugin.ex b/lib/lsg_irc/wikipedia_plugin.ex
index 16e7c42..618eb66 100644
--- a/lib/lsg_irc/wikipedia_plugin.ex
+++ b/lib/lsg_irc/wikipedia_plugin.ex
@@ -11,11 +11,11 @@ defmodule LSG.IRC.WikipediaPlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:wp", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:wp", [plugin: __MODULE__])
{:ok, nil}
end
diff --git a/lib/lsg_irc/wolfram_alpha_plugin.ex b/lib/lsg_irc/wolfram_alpha_plugin.ex
index b34db56..c07f659 100644
--- a/lib/lsg_irc/wolfram_alpha_plugin.ex
+++ b/lib/lsg_irc/wolfram_alpha_plugin.ex
@@ -11,11 +11,11 @@ defmodule LSG.IRC.WolframAlphaPlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:wa", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:wa", [plugin: __MODULE__])
{:ok, nil}
end
diff --git a/lib/lsg_irc/youtube_plugin.ex b/lib/lsg_irc/youtube_plugin.ex
index e0781f0..49fc31c 100644
--- a/lib/lsg_irc/youtube_plugin.ex
+++ b/lib/lsg_irc/youtube_plugin.ex
@@ -12,12 +12,11 @@ defmodule LSG.IRC.YouTubePlugin do
def irc_doc, do: @moduledoc
def start_link() do
- GenServer.start_link(__MODULE__, [])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:yt", [])
- {:ok, _} = Registry.register(IRC.PubSub, "trigger:youtube", [])
+ for t <- ["trigger:yt", "trigger:youtube"], do: {:ok, _} = Registry.register(IRC.PubSub, t, [plugin: __MODULE__])
{:ok, %__MODULE__{}}
end
diff --git a/lib/lsg_web.ex b/lib/lsg_web.ex
index f5dee6f..113d00d 100644
--- a/lib/lsg_web.ex
+++ b/lib/lsg_web.ex
@@ -17,6 +17,10 @@ defmodule LSGWeb do
and import those modules here.
"""
+ def format_chan("##") do
+ "♯♯"
+ end
+
def format_chan("#") do
"♯"
end
@@ -26,7 +30,10 @@ defmodule LSGWeb do
end
def reformat_chan("♯") do
- ""
+ "#"
+ end
+ def reformat_chan("♯♯") do
+ "##"
end
def reformat_chan(chan) do
diff --git a/lib/lsg_web/controllers/alcoolog_controller.ex b/lib/lsg_web/controllers/alcoolog_controller.ex
index b88faa3..6542f15 100644
--- a/lib/lsg_web/controllers/alcoolog_controller.ex
+++ b/lib/lsg_web/controllers/alcoolog_controller.ex
@@ -16,6 +16,166 @@ defmodule LSGWeb.AlcoologController do
end
end
+ def nick(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ profile_account = IRC.Account.find_always_by_nick(network, nick, nick)
+ days = String.to_integer(Map.get(params, "days", "180"))
+ friend? = Enum.member?(IRC.Membership.friends(account), profile_account.id)
+ if friend? do
+ stats = LSG.IRC.AlcoologPlugin.get_full_statistics(profile_account.id)
+ history = for {{nick, ts}, points, active, cl, deg, type, descr, meta} <- LSG.IRC.AlcoologPlugin.nick_history(profile_account) do
+ %{
+ at: ts |> DateTime.from_unix!(:millisecond),
+ points: points,
+ active: active,
+ cl: cl,
+ deg: deg,
+ type: type,
+ description: descr,
+ meta: meta
+ }
+ end
+ history = Enum.sort(history, &(DateTime.compare(&1.at, &2.at) != :lt))
+ |> IO.inspect()
+ conn
+ |> assign(:title, "alcoolog #{nick}")
+ |> render("user.html", network: network, profile: profile_account, days: days, nick: nick, history: history, stats: stats)
+ else
+ conn
+ |> put_status(404)
+ |> text("Page not found")
+ end
+ end
+
+ def nick_stats_json(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ profile_account = IRC.Account.find_always_by_nick(network, nick, nick)
+ friend? = Enum.member?(IRC.Membership.friends(account), profile_account.id)
+ if friend? do
+ stats = LSG.IRC.AlcoologPlugin.get_full_statistics(profile_account.id)
+
+ conn
+ |> put_resp_content_type("application/json")
+ |> text(Jason.encode!(stats))
+ else
+ conn
+ |> put_status(404)
+ |> json([])
+ end
+ end
+
+ def nick_gls_json(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ profile_account = IRC.Account.find_always_by_nick(network, nick, nick)
+ friend? = Enum.member?(IRC.Membership.friends(account), profile_account.id)
+ count = String.to_integer(Map.get(params, "days", "180"))
+ if friend? do
+ data = LSG.IRC.AlcoologPlugin.user_over_time_gl(profile_account, count)
+ delay = count*((24 * 60)*60)
+ now = DateTime.utc_now()
+ start_date = DateTime.utc_now()
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
+ |> DateTime.to_date()
+ |> Date.to_erl()
+ filled = (:calendar.date_to_gregorian_days(start_date) .. :calendar.date_to_gregorian_days(DateTime.utc_now |> DateTime.to_date() |> Date.to_erl))
+ |> Enum.to_list
+ |> Enum.map(&(:calendar.gregorian_days_to_date(&1)))
+ |> Enum.map(&Date.from_erl!(&1))
+ |> Enum.map(fn(date) ->
+ %{date: date, gls: Map.get(data, date, 0)}
+ end)
+ |> Enum.sort(&(Date.compare(&1.date, &2.date) != :gt))
+
+ conn
+ |> put_resp_content_type("application/json")
+ |> text(Jason.encode!(filled))
+ else
+ conn
+ |> put_status(404)
+ |> json([])
+ end
+ end
+
+
+
+ def nick_volumes_json(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ profile_account = IRC.Account.find_always_by_nick(network, nick, nick)
+ friend? = Enum.member?(IRC.Membership.friends(account), profile_account.id)
+ count = String.to_integer(Map.get(params, "days", "180"))
+ if friend? do
+ data = LSG.IRC.AlcoologPlugin.user_over_time(profile_account, count)
+ delay = count*((24 * 60)*60)
+ now = DateTime.utc_now()
+ start_date = DateTime.utc_now()
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
+ |> DateTime.to_date()
+ |> Date.to_erl()
+ filled = (:calendar.date_to_gregorian_days(start_date) .. :calendar.date_to_gregorian_days(DateTime.utc_now |> DateTime.to_date() |> Date.to_erl))
+ |> Enum.to_list
+ |> Enum.map(&(:calendar.gregorian_days_to_date(&1)))
+ |> Enum.map(&Date.from_erl!(&1))
+ |> Enum.map(fn(date) ->
+ %{date: date, volumes: Map.get(data, date, 0)}
+ end)
+ |> Enum.sort(&(Date.compare(&1.date, &2.date) != :gt))
+
+ conn
+ |> put_resp_content_type("application/json")
+ |> text(Jason.encode!(filled))
+ else
+ conn
+ |> put_status(404)
+ |> json([])
+ end
+ end
+
+ def nick_log_json(conn = %{assigns: %{account: account}}, %{"network" => network, "nick" => nick}) do
+ profile_account = IRC.Account.find_always_by_nick(network, nick, nick)
+ friend? = Enum.member?(IRC.Membership.friends(account), profile_account.id)
+ if friend? do
+ history = for {{nick, ts}, points, active, cl, deg, type, descr, meta} <- LSG.IRC.AlcoologPlugin.nick_history(profile_account) do
+ %{
+ at: ts |> DateTime.from_unix!(:millisecond) |> DateTime.to_iso8601(),
+ points: points,
+ active: active,
+ cl: cl,
+ deg: deg,
+ type: type,
+ description: descr,
+ meta: meta
+ }
+ end
+ last = List.last(history)
+ {_, active} = LSG.IRC.AlcoologPlugin.user_stats(profile_account)
+ last = %{last | active: active, at: DateTime.utc_now() |> DateTime.to_iso8601()}
+ history = history ++ [last]
+
+ conn
+ |> put_resp_content_type("application/json")
+ |> text(Jason.encode!(history))
+ else
+ conn
+ |> put_status(404)
+ |> json([])
+ end
+ end
+
+ def nick_history_json(conn = %{assigns: %{account: account}}, %{"network" => network, "nick" => nick}) do
+ profile_account = IRC.Account.find_always_by_nick(network, nick, nick)
+ friend? = Enum.member?(IRC.Membership.friends(account), profile_account.id)
+ if friend? do
+ history = for {_, date, value} <- LSG.IRC.AlcoologAnnouncerPlugin.log(profile_account) do
+ %{date: DateTime.to_iso8601(date), value: value}
+ end
+ conn
+ |> put_resp_content_type("application/json")
+ |> text(Jason.encode!(history))
+ else
+ conn
+ |> put_status(404)
+ |> json([])
+ end
+ end
+
def index(conn = %{assigns: %{account: account}}, %{"network" => network, "chan" => channel}) do
index(conn, account, network, LSGWeb.reformat_chan(channel))
end
@@ -40,16 +200,22 @@ defmodule LSGWeb.AlcoologController do
#end
def index(conn, account, network, channel) do
- aday = 18*((24 * 60)*60)
+ aday = ((24 * 60)*60)
now = DateTime.utc_now()
- before = now
- |> DateTime.add(-aday, :second)
- |> DateTime.to_unix(:millisecond)
+ before7 = now
+ |> DateTime.add(-(7*aday), :second)
+ |> DateTime.to_unix(:millisecond)
+ before15 = now
+ |> DateTime.add(-(15*aday), :second)
+ |> DateTime.to_unix(:millisecond)
+ before31 = now
+ |> DateTime.add(-(31*aday), :second)
+ |> DateTime.to_unix(:millisecond)
#match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end)
match = [
- {{{:_, :"$1"}, :_, :_, :_, :_},
+ {{{:_, :"$1"}, :_, :_, :_, :_, :_, :_, :_},
[
- {:>, :"$1", {:const, before}},
+ {:>, :"$1", {:const, before15}},
], [:"$_"]}
]
@@ -58,13 +224,13 @@ defmodule LSGWeb.AlcoologController do
members_ids = Enum.map(members, fn({account, _, nick}) -> account.id end)
member_names = Enum.reduce(members, %{}, fn({account, _, nick}, acc) -> Map.put(acc, account.id, nick) end)
drinks = :ets.select(LSG.IRC.AlcoologPlugin.ETS, match)
- |> Enum.filter(fn({{account, _}, _, _, _, _}) -> Enum.member?(members_ids, account) end)
- |> Enum.map(fn({{account, _}, _, _, _, _} = object) -> {object, Map.get(member_names, account)} end)
- |> Enum.sort_by(fn({{{_, ts}, _, _, _, _}, _}) -> ts end, &>/2)
+ |> Enum.filter(fn({{account, _}, _vol, _cur, _cl, _deg, _name, _cmt, _meta}) -> Enum.member?(members_ids, account) end)
+ |> Enum.map(fn({{account, _}, _, _, _, _, _, _, _} = object) -> {object, Map.get(member_names, account)} end)
+ |> Enum.sort_by(fn({{{_, ts}, _, _, _, _, _, _, _}, _}) -> ts end, &>/2)
stats = LSG.IRC.AlcoologPlugin.get_channel_statistics(account, network, channel)
- top = Enum.reduce(drinks, %{}, fn({{{account_id, _}, vol, _, _, _}, _}, acc) ->
+ top = Enum.reduce(drinks, %{}, fn({{{account_id, _}, vol, _, _, _, _, _, _}, _}, acc) ->
nick = Map.get(member_names, account_id)
all = Map.get(acc, nick, 0)
Map.put(acc, nick, all + vol)
@@ -77,4 +243,81 @@ defmodule LSGWeb.AlcoologController do
|> render("index.html", network: network, channel: channel, drinks: drinks, top: top, stats: stats)
end
+ def index_gls_json(conn = %{assigns: %{account: account}}, %{"network" => network, "chan" => channel}) do
+ count = 30
+ channel = LSGWeb.reformat_chan(channel)
+ members = IRC.Membership.expanded_members_or_friends(account, network, channel)
+ members_ids = Enum.map(members, fn({account, _, nick}) -> account.id end)
+ member_names = Enum.reduce(members, %{}, fn({account, _, nick}, acc) -> Map.put(acc, account.id, nick) end)
+ delay = count*((24 * 60)*60)
+ now = DateTime.utc_now()
+ start_date = DateTime.utc_now()
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
+ |> DateTime.to_date()
+ |> Date.to_erl()
+ filled = (:calendar.date_to_gregorian_days(start_date) .. :calendar.date_to_gregorian_days(DateTime.utc_now |> DateTime.to_date() |> Date.to_erl))
+ |> Enum.to_list
+ |> Enum.map(&(:calendar.gregorian_days_to_date(&1)))
+ |> Enum.map(&Date.from_erl!(&1))
+ |> Enum.map(fn(date) ->
+ {date, (for {a, _, _} <- members, into: Map.new, do: {Map.get(member_names, a.id, a.id), 0})}
+ end)
+ |> Enum.into(Map.new)
+
+ gls = Enum.reduce(members, filled, fn({account, _, _}, gls) ->
+ Enum.reduce(LSG.IRC.AlcoologPlugin.user_over_time_gl(account, count), gls, fn({date, gl}, gls) ->
+ u = Map.get(gls, date, %{})
+ |> Map.put(Map.get(member_names, account.id, account.id), gl)
+ Map.put(gls, date, u)
+ end)
+ end)
+
+ dates = (:calendar.date_to_gregorian_days(start_date) .. :calendar.date_to_gregorian_days(DateTime.utc_now |> DateTime.to_date() |> Date.to_erl))
+ |> Enum.to_list
+ |> Enum.map(&(:calendar.gregorian_days_to_date(&1)))
+ |> Enum.map(&Date.from_erl!(&1))
+
+ filled2 = Enum.map(member_names, fn({_, name}) ->
+ history = (:calendar.date_to_gregorian_days(start_date) .. :calendar.date_to_gregorian_days(DateTime.utc_now |> DateTime.to_date() |> Date.to_erl))
+ |> Enum.to_list
+ |> Enum.map(&(:calendar.gregorian_days_to_date(&1)))
+ |> Enum.map(&Date.from_erl!(&1))
+ |> Enum.map(fn(date) ->
+ get_in(gls, [date, name]) #%{date: date, gl: get_in(gls, [date, name])}
+ end)
+ if Enum.all?(history, fn(x) -> x == 0 end) do
+ nil
+ else
+ %{name: name, history: history}
+ end
+ end)
+ |> Enum.filter(fn(x) -> x end)
+
+ conn
+ |> put_resp_content_type("application/json")
+ |> text(Jason.encode!(%{labels: dates, data: filled2}))
+ end
+
+ def minisync(conn, %{"user_id" => user_id, "key" => key, "value" => value}) do
+ account = IRC.Account.get(user_id)
+ if account do
+ ds = LSG.IRC.AlcoologPlugin.data_state()
+ meta = LSG.IRC.AlcoologPlugin.get_user_meta(ds, account.id)
+ case Float.parse(value) do
+ {val, _} ->
+ new_meta = Map.put(meta, String.to_existing_atom(key), val)
+ LSG.IRC.AlcoologPlugin.put_user_meta(ds, account.id, new_meta)
+ _ ->
+ conn
+ |> put_status(:unprocessable_entity)
+ |> text("invalid value")
+ end
+ else
+ conn
+ |> put_status(:not_found)
+ |> text("not found")
+ end
+ end
+
end
diff --git a/lib/lsg_web/controllers/irc_controller.ex b/lib/lsg_web/controllers/irc_controller.ex
index e0bf24d..317bb27 100644
--- a/lib/lsg_web/controllers/irc_controller.ex
+++ b/lib/lsg_web/controllers/irc_controller.ex
@@ -52,11 +52,16 @@ defmodule LSGWeb.IrcController do
conn.assigns[:chan] -> "/#{conn.assigns.network}/#{LSGWeb.format_chan(conn.assigns.chan)}"
true -> "/-"
end
- if Map.has_key?(data, txt) do
+ if lines = Map.get(data, txt) do
+ lines = Enum.map(lines, fn(line) ->
+ line
+ |> String.split("\\\\")
+ |> Enum.intersperse(Phoenix.HTML.Tag.tag(:br))
+ end)
conn
|> assign(:breadcrumbs, [{"txt", "#{base_url}/txt"}])
|> assign(:title, "#{txt}.txt")
- |> render("txt.html", name: txt, data: data[txt], doc: nil)
+ |> render("txt.html", name: txt, data: lines, doc: nil)
else
conn
|> put_status(404)
diff --git a/lib/lsg_web/router.ex b/lib/lsg_web/router.ex
index de7fafb..d681ed9 100644
--- a/lib/lsg_web/router.ex
+++ b/lib/lsg_web/router.ex
@@ -33,13 +33,20 @@ defmodule LSGWeb.Router do
get "/-/alcoolog", AlcoologController, :index
get "/-/alcoolog/~/:account_name", AlcoologController, :index
get "/:network", NetworkController, :index
+ get "/:network/~:nick/alcoolog", AlcoologController, :nick
+ get "/:network/~:nick/alcoolog/log.json", AlcoologController, :nick_log_json
+ get "/:network/~:nick/alcoolog/gls.json", AlcoologController, :nick_gls_json
+ get "/:network/~:nick/alcoolog/volumes.json", AlcoologController, :nick_volumes_json
+ get "/:network/~:nick/alcoolog/history.json", AlcoologController, :nick_history_json
+ get "/:network/~:nick/alcoolog/stats.json", AlcoologController, :nick_stats_json
+ get "/:network/:chan/alcoolog", AlcoologController, :index
+ get "/:network/:chan/alcoolog/gls.json", AlcoologController, :index_gls_json
+ put "/api/alcoolog/minisync/:user_id/meta/:key", AlcoologController, :minisync_put_meta
get "/:network/:chan", IrcController, :index
get "/:network/:chan/txt", IrcController, :txt
get "/:network/:chan/txt/:name", IrcController, :txt
get "/:network/:channel/preums", IrcController, :preums
- get "/:network/:chan/alcoolog", AlcoologController, :index
get "/:network/:chan/alcoolog/t/:token", AlcoologController, :token
- get "/:network/alcoolog/~/:nick", AlcoologController, :nick
end
end
diff --git a/lib/lsg_web/templates/alcoolog/index.html.eex b/lib/lsg_web/templates/alcoolog/index.html.eex
index e656e64..3f522e9 100644
--- a/lib/lsg_web/templates/alcoolog/index.html.eex
+++ b/lib/lsg_web/templates/alcoolog/index.html.eex
@@ -5,54 +5,202 @@ ol li {
</style>
<%= if @stats == [] do %>
- </strong><i>:o personne ne boit</i></strong>
+ <div class="rounded-md bg-red-50 p-4">
+ <div class="flex">
+ <div class="flex-shrink-0">
+ <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
+ </svg>
+ </div>
+ <div class="ml-3">
+ <h3 class="text-sm leading-5 font-medium text-red-800">
+ CATASTROPHE! Personne n'a bu!!!!
+ </h3>
+ </div>
+ </div>
+ </div>
<% end %>
-<ul>
+<ul class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
<%= for {nick, status} <- @stats do %>
- <li><strong><%= nick %> <%= status.user_status %> - <%= status.trend_symbol %> <%= status.active %> g/l</strong><br/>
- &mdash; 15m: <%= status.active15m %> g/l - 30m: <%= status.active30m %> g/l - 1h: <%= status.active1h %> g/l<br />
- &mdash; dernier verre: <%= status.last_type %> <%= status.last_descr %> (<%= status.last_points %>)
- <%= LSGWeb.LayoutView.format_time(status.last_at) %>
- <br />
- &mdash; sobre dans: <%= status.sober_in_s %>
- <br />
- <small>
- &mdash; aujourd'hui: <%= status.daily_volumes %> points, <%= status.daily_gl %> g/l
- </small>
+ <li class="col-span-1 bg-white rounded-lg shadow">
+ <div class="w-full flex items-center justify-between p-6 space-x-6">
+ <div class="flex-1 truncate">
+ <div class="flex items-center space-x-3">
+ <h3 class="text-gray-900 text-base leading-5 font-semibold truncate"><%= link nick, to: alcoolog_path(@conn, :nick, @network, nick) %></h3>
+ <% rising_class = if status.rising, do: "teal", else: "red" %>
+ <span class="flex-shrink-0 inline-block px-2 py-0.5 text-<%= rising_class %>-800 text-sm leading-4 font-medium bg-<%= rising_class %>-100 rounded-full">
+ <%= status.trend_symbol %> <%= Float.round(status.active, 4) %> g/l
+ </span>
+ </div>
+ <p class="mt-1 text-gray-700 text-sm leading-5 truncate">
+ <span class="text-base"><%= status.last_cl %>cl @ <%= status.last_deg %>°</span>
+ <%= if status.last_descr && status.last_descr != "" do %>
+ <br /><%= status.last_descr %>
+ <% end %>
+ <br/><small><%= LSGWeb.LayoutView.format_time(status.last_at) %></small>
+ </p>
+
+ <p class="mt-1 text-gray-500 text-sm leading-5 truncate">
+ <br />
+ &mdash; sobre dans: <%= status.sober_in_s %><br />
+ <%= if status.since do %>
+ &mdash; depuis: <%= status.since_s %><br />
+ <% end %>
+ <small>
+ &mdash; 15m: <%= status.active15m %> g/l - 30m: <%= status.active30m %> g/l - 1h: <%= status.active1h %> g/l<br />
+ &mdash; aujourd'hui: <%= status.daily_volumes %> points, <%= status.daily_gl %> g/l
+ </small>
+ </p>
+
+
+ </div>
+ </div>
</li>
<% end %>
</ul>
<%= if @stats == %{} do %>
- <strong><i>... et personne n'a bu :o :o :o</i></strong>
+ <div class="rounded-md bg-red-50 p-4">
+ <div class="flex">
+ <div class="flex-shrink-0">
+ <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
+ </svg>
+ </div>
+ <div class="ml-3">
+ <h3 class="text-sm leading-5 font-medium text-red-800">
+ ENCORE PIRE! Aucune boisson enregistrée!
+ </h3>
+ </div>
+ </div>
+ </div>
<% else %>
- <p>
- top consommateur par volume, les 7 derniers jours: <%= Enum.intersperse(for({nick, count} <- @top, do: "#{nick}: #{Float.round(count,4)}"), ", ") %>
- </p>
-
- <table class="table">
- <thead>
- <tr>
- <th scope="col">date</th>
- <th scope="col">nick</th>
- <th scope="col">points</th>
- <th scope="col">nom</th>
- </tr>
- </thead>
- <tbody>
- <%= for {{{account, date}, points, _, nom, comment}, nick} <- @drinks do %>
- <% date = DateTime.from_unix!(date, :millisecond) %>
- <th scope="row"><%= LSGWeb.LayoutView.format_time(date, false) %></th>
- <td><%= nick %></td>
- <td><%= Float.round(points+0.0, 5) %></td>
- <td><%= nom||"" %> <%= comment||"" %></td>
- </tr>
- <% end %>
- </tbody>
- </table>
+
+<canvas id="myChart" class="w-full" height="200"></canvas>
+
+ <h2 class="leading-8 m-4 font-semibold text-2xl">Classement <span class="font-medium text-gray-600 text-lg">15 jours</span></h2>
+
+<ul class="grid grid-cols-1 gap-6 sm:grid-cols-5 lg:grid-cols-5">
+ <%= for {{nick, count}, rank} <- Enum.with_index(@top) do %>
+ <% rank = rank + 1 %>
+ <% trophy = rank <= 3 %>
+ <% {colour, text} = case rank do
+1 -> {"yellow-500", "font-semibold text-base"}
+2 -> {"gray-500", "font-medium text-base"}
+3 -> {"orange-300", "font-medium text-base"}
+_ -> {"gray-300", ""}
+ end %>
+ <li class="col-span-1 bg-white rounded-lg shadow text-center <%= text %>">
+ <%= if trophy do %>
+ <span class="text-<%= colour %>">
+ <svg style="width: 1.125em; height: 1.5em; display: inline-block;" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="trophy" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" data-fa-i2svg=""><path fill="currentColor" d="M552 64H448V24c0-13.3-10.7-24-24-24H152c-13.3 0-24 10.7-24 24v40H24C10.7 64 0 74.7 0 88v56c0 35.7 22.5 72.4 61.9 100.7 31.5 22.7 69.8 37.1 110 41.7C203.3 338.5 240 360 240 360v72h-48c-35.3 0-64 20.7-64 56v12c0 6.6 5.4 12 12 12h296c6.6 0 12-5.4 12-12v-12c0-35.3-28.7-56-64-56h-48v-72s36.7-21.5 68.1-73.6c40.3-4.6 78.6-19 110-41.7 39.3-28.3 61.9-65 61.9-100.7V88c0-13.3-10.7-24-24-24zM99.3 192.8C74.9 175.2 64 155.6 64 144v-16h64.2c1 32.6 5.8 61.2 12.8 86.2-15.1-5.2-29.2-12.4-41.7-21.4zM512 144c0 16.1-17.7 36.1-35.3 48.8-12.5 9-26.7 16.2-41.8 21.4 7-25 11.8-53.6 12.8-86.2H512v16z"></path></svg>
+ </span>
+ <% end %>
+ #<%= rank %>: <%= link nick, to: alcoolog_path(@conn, :nick, @network, nick) %>
+ <br />
+ <span class="text-xs"><%= Float.round(count, 4) %></span>
+ </li>
+ <% end %>
+</ul>
+
+ <h2 class="leading-8 m-4 font-semibold text-2xl">Historique</h2>
+<div class="flex flex-col">
+ <div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
+ <div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200">
+ <table class="min-w-full divide-y divide-gray-200">
+ <thead>
+ <tr>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
+ date
+ </th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
+ nick
+ </th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
+ &nbsp;
+ </th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
+ &nbsp;
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <%= for {{{{account, date}, points, _active, cl, deg, nom, comment, _meta}, nick}, index} <- Enum.with_index(@drinks) do %>
+ <% class = if(Integer.is_even(index), do: "bg-gray-50", else: "bg-white") %>
+ <% date = DateTime.from_unix!(date, :millisecond) %>
+ <tr class="<%= class %>">
+ <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
+ <%= LSGWeb.LayoutView.format_time(date, false) %>
+ </td>
+ <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
+ <%= link nick, to: alcoolog_path(@conn, :nick, @network, nick) %>
+ </td>
+ <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500"><%= cl %>cl <%= deg %>°</td>
+ <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500"><%= comment||"" %></td>
+ </tr>
+ <% end %>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
<% end %>
<%= if @conn.assigns.account && (@network || @channel) do %>
<%= link("alcoolog global", to: alcoolog_path(@conn, :index)) %>
<% end %>
+
+<link href='/assets/css/metricsgraphics.css' rel='stylesheet' type='text/css'>
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.css" integrity="sha512-SUJFImtiT87gVCOXl3aGC00zfDl6ggYAw5+oheJvRJ8KBXZrr/TMISSdVJ5bBarbQDRC2pR5Kto3xTR0kpZInA==" crossorigin="anonymous" />
+<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js" integrity="sha512-vBmx0N/uQOXznm/Nbkp7h0P1RfLSj0HQrFSzV8m7rOGyj30fYAOKHYvCNez+yM8IrfnW0TCodDEjRqf6fodf/Q==" crossorigin="anonymous"></script>
+<script src="/assets/js/jquery3.1.0.min.js"></script>
+<script src="/assets/js/d3.v4.min.js"></script>
+<script src="/assets/js/metricsgraphics.min.js"></script>
+ <script type="text/javascript">
+ (function() {
+ // oui s trè moch :( :( :(
+var ctx = document.getElementById('myChart').getContext('2d');
+ d3.json('<%= alcoolog_path(@conn, :index_gls_json, @network, LSGWeb.format_chan(@channel)) %>', function(data) {
+
+ var dynamicColors = function() {
+ var r = Math.floor(Math.random() * 255);
+ var g = Math.floor(Math.random() * 255);
+ var b = Math.floor(Math.random() * 255);
+ return "rgb(" + r + "," + g + "," + b + ")";
+ };
+
+ labels = data.labels;
+ datasets = $.map($.makeArray(data.data), function(set) {
+ return {label: set.name, data: set.history, fill: false, borderColor: dynamicColors()}
+ });
+
+
+var myChart = new Chart(ctx, {
+ type: 'line',
+ data: {
+ labels: labels,
+ datasets: datasets
+ },
+ options: {
+ scales: {
+ xAxes: [{
+ type: 'time',
+ time: {
+ unit: 'day'
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ beginAtZero: true
+ }
+ }]
+ }
+ }
+});
+});
+
+
+})();
+</script>
+
diff --git a/lib/lsg_web/templates/alcoolog/user.html.eex b/lib/lsg_web/templates/alcoolog/user.html.eex
new file mode 100644
index 0000000..3b2b20b
--- /dev/null
+++ b/lib/lsg_web/templates/alcoolog/user.html.eex
@@ -0,0 +1,170 @@
+<%= if @stats.active > 0 do %>
+ <h3 class="text-gray-900 text-xl leading-5 font-semibold truncate">
+ <% rising_class = if @stats.rising, do: "teal", else: "red" %>
+ <span class="flex-shrink-0 inline-block px-3 py-3 text-<%= rising_class %>-800 text-2xl leading-4 font-medium bg-<%= rising_class %>-100 rounded-full">
+ <%= @stats.trend_symbol %> <%= Float.round(@stats.active, 4) %> g/l
+ </span>
+
+ <span class="px-3"><%= @stats.last_cl %>cl @ <%= @stats.last_deg %>°</span>
+
+ <span class="px-3 text-lg text-gray-700"><%= if @stats.last_descr && @stats.last_descr != "" do %>
+ <%= @stats.last_descr %>
+ <% end %>
+ </span>
+
+ <span class="px-3 text-gray-500 text-sm font-thin"><%= LSGWeb.LayoutView.format_time(@stats.last_at) %></span>
+
+ </h3>
+
+ <p class="text-gray-500 font-thin text-lg">
+ a commencé il y a <span class="text-gray-700 font-semibold"><%= @stats.since_s %></span>
+ &mdash;
+ sobre dans <span class="text-gray-700 font-semibold"><%= @stats.sober_in_s %></span>
+ </p>
+<% else %>
+ <h3 class="text-gray-900 text-xl leading-5 font-semibold truncate">
+ est sobre!
+
+ <p class="text-gray-500 font-thin text-lg">
+ dernier verre
+ <span class="px-3"><%= @stats.last_cl %>cl @ <%= @stats.last_deg %>°</span>
+
+ <span class="px-3 text-lg text-gray-700"><%= if @stats.last_descr && @stats.last_descr != "" do %>
+ <%= @stats.last_descr %>
+ <% end %>
+ </span>
+
+ <span class="px-3 text-gray-500 text-sm font-thin"><%= LSGWeb.LayoutView.format_time(@stats.last_at) %></span>
+ </h3>
+<% end %>
+
+<canvas id="myChart" class="w-full" height="200"></canvas>
+<canvas id="myChartGl" class="w-full" height="200"></canvas>
+
+
+<h2 class="leading-8 m-4 font-semibold text-2xl">Historique</h2>
+<div class="flex flex-col">
+ <div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
+ <div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200">
+ <table class="min-w-full divide-y divide-gray-200">
+ <thead>
+ <tr>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
+ date
+ </th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
+ &nbsp;
+ </th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
+ &nbsp;
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <%= for {%{at: date, cl: cl, deg: deg, description: comment}, index} <- Enum.with_index(@history) do %>
+ <% class = if(Integer.is_even(index), do: "bg-gray-50", else: "bg-white") %>
+ <tr class="<%= class %>">
+ <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
+ <%= LSGWeb.LayoutView.format_time(date, false) %>
+ </td>
+ <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500"><%= cl %>cl <%= deg %>°</td>
+ <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500"><%= comment||"" %></td>
+ </tr>
+ <% end %>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div id="metrics-gl" class="w-full"></div>
+
+<link href='/assets/css/metricsgraphics.css' rel='stylesheet' type='text/css'>
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.css" integrity="sha512-SUJFImtiT87gVCOXl3aGC00zfDl6ggYAw5+oheJvRJ8KBXZrr/TMISSdVJ5bBarbQDRC2pR5Kto3xTR0kpZInA==" crossorigin="anonymous" />
+<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js" integrity="sha512-vBmx0N/uQOXznm/Nbkp7h0P1RfLSj0HQrFSzV8m7rOGyj30fYAOKHYvCNez+yM8IrfnW0TCodDEjRqf6fodf/Q==" crossorigin="anonymous"></script>
+<script src="/assets/js/jquery3.1.0.min.js"></script>
+<script src="/assets/js/d3.v4.min.js"></script>
+<script src="/assets/js/metricsgraphics.min.js"></script>
+ <script type="text/javascript">
+ (function() {
+var ctx = document.getElementById('myChart').getContext('2d');
+ d3.json('<%= alcoolog_path(@conn, :nick_volumes_json, @network, @nick, days: @days) %>', function(data) {
+var myChart = new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: data.map(function(data) { return new Date(data.date) }),
+ datasets: [{
+ label: 'Volumes d\'alcool',
+ data: data.map(function(data) { return data.volumes }),
+ borderWidth: 1
+ }]
+ },
+ options: {
+ scales: {
+ xAxes: [{
+ type: 'time',
+ time: {
+ unit: 'day'
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ beginAtZero: true
+ }
+ }]
+ }
+ }
+});
+});
+
+
+
+
+var ctxgl = document.getElementById('myChartGl').getContext('2d');
+ d3.json('<%= alcoolog_path(@conn, :nick_gls_json, @network, @nick, days: @days) %>', function(data) {
+var myChartgl = new Chart(ctxgl, {
+ type: 'bar',
+ data: {
+ labels: data.map(function(data) { return new Date(data.date) }),
+ datasets: [{
+ label: 'g/l ingérés',
+ data: data.map(function(data) { return data.gls }),
+ borderWidth: 1
+ }]
+ },
+ options: {
+ scales: {
+ xAxes: [{
+ type: 'time',
+ time: {
+ unit: 'day'
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ beginAtZero: true
+ }
+ }]
+ }
+ }
+});
+});
+
+
+ d3.json('<%= alcoolog_path(@conn, :nick_log_json, @network, @nick, days: @days) %>', function(data) {
+ data = data.map(function(d) {
+ date = new Date(d.at);
+ return {"date": date, "active": d.active};
+ });
+ MG.data_graphic({
+ title: "g/l, All Time",
+ data: data,
+ width: 1000,
+ height: 400,
+ target: document.getElementById('metrics-gl'),
+ x_accessor: 'date',
+ y_accessor: 'active',
+ });
+ });
+})();
+</script>
diff --git a/lib/lsg_web/templates/irc/index.html.eex b/lib/lsg_web/templates/irc/index.html.eex
index 9e4f724..a8544b3 100644
--- a/lib/lsg_web/templates/irc/index.html.eex
+++ b/lib/lsg_web/templates/irc/index.html.eex
@@ -1,19 +1,12 @@
-<style type="text/css">
-.help-entry h1 {
- font-size: 18px;
-}
-.help-entry h2 {
- font-size: 16px;
-}
-</style>
-
-<p>
+<div class="hidden sm:block">
+ <nav class="flex flex-wrap content-center">
<% list = for {identifier, _} <- @commands do %>
<% name = String.replace(identifier, "_", " ") %>
- <%= link(name, to: "##{identifier}") %>
+ <%= link(name, to: "##{identifier}", class: "px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %>
<% end %>
- <small><%= Enum.intersperse(list, " - ") %></small>
-</p>
+ <%= list %>
+ </nav>
+</div>
<%= if @members != [] do %>
<% users = for {_acc, user, nick} <- @members, do: if(user, do: nick, else: content_tag(:i, nick)) %>
@@ -22,10 +15,12 @@
<% end %>
<% end %>
-<div class="irchelps">
+<div class="irchelps space-y-6">
<%= for {identifier, help} <- @commands do %>
<%= if help do %>
- <div class="help-entry" id="<%= identifier %>"><%= LSGWeb.LayoutView.liquid_markdown(@conn, help) %></div>
+ <div class="bg-white border-b border-gray-200" id="<%= identifier %>">
+ <div class="prose w-auto max-w-full"><%= LSGWeb.LayoutView.liquid_markdown(@conn, help) %></div>
+ </div>
<% end %>
<% end %>
</div>
diff --git a/lib/lsg_web/templates/irc/txt.html.eex b/lib/lsg_web/templates/irc/txt.html.eex
index e9df681..fd4ea00 100644
--- a/lib/lsg_web/templates/irc/txt.html.eex
+++ b/lib/lsg_web/templates/irc/txt.html.eex
@@ -1,15 +1,27 @@
<style type="text/css">
-ol li {
- margin-bottom: 5px
+:target {
+ background-color: #ffa;
+ font-weight: bold;
+}
+ol > li > a {
+ display: none;
+}
+ol > li:hover > a {
+ display: inline-block;
+ color: gray;
}
</style>
-<ol>
+<ol class="prose space-y-3 w-auto max-w-full list-outside list-decimal m-4 leading-loose">
<%= for {txt, id} <- Enum.with_index(@data) do %>
- <li id="<%= @name %>-<%= id %>"><%= txt %></li>
+ <li class="text-lg" id="q<%= id+1 %>">
+
+ <%= txt %>
+ <a href="#q<%= id+1 %>" class="text-xs text-gray-500 space-x-3">#</a>
+ </li>
<% end %>
</ol>
<p>
- <a href="/irc/txt/<%= @name %>.txt">télécharger au format texte</a>
+ <a href="/-/txt/<%= @name %>.txt" class="text-xs text-gray-500">télécharger au format texte</a>
</p>
diff --git a/lib/lsg_web/templates/irc/txts.html.eex b/lib/lsg_web/templates/irc/txts.html.eex
index 5971ffa..66321b5 100644
--- a/lib/lsg_web/templates/irc/txts.html.eex
+++ b/lib/lsg_web/templates/irc/txts.html.eex
@@ -1,25 +1,26 @@
-<style type="text/css">
-.help-entry h1 {
- font-size: 18px;
-}
-.help-entry h2 {
- font-size: 16px;
-}
-</style>
-
-<p><strong><%= @lines %></strong> lignes dans <strong><%= @files %></strong> fichiers.
-<a href="#help">Aide</a>.
-</p>
-
-<ul>
+<div>
+ <h2 class="text-gray-500 text-xs font-medium uppercase tracking-wide">
+ <strong><%= @lines %></strong> lignes dans <strong><%= @files %></strong> fichiers
+ &mdash;
+ <a href="#help">Aide</a>
+ </h2>
+ <ul class="mt-3 grid grid-cols-1 gap-5 sm:gap-6 sm:grid-cols-4 lg:grid-cols-6">
<%= for {txt, data} <- @data do %>
<% base_url = cond do
@conn.assigns[:chan] -> "/#{@conn.assigns.network}/#{LSGWeb.format_chan(@conn.assigns.chan)}"
true -> "/-"
end %>
- <li><a href="<%= base_url %>/txt/<%= txt %>"><%= txt %></a> <i>(<%= Enum.count(data) %>)</i></li>
+ <li class="col-span-1 flex shadow-sm rounded-m">
+ <div class="flex-1 flex items-center justify-between border-l border-t border-r border-b border-gray-200 bg-white rounded-md truncate">
+ <div class="flex-1 px-4 py-2 text-sm leading-5 truncate">
+ <a href="<%= base_url %>/txt/<%= txt %>" class="text-gray-900 text-lg font-medium hover:text-gray-600 transition ease-in-out duration-150"><%= txt %></a>
+ <p class="text-gray-500"><%= Enum.count(data) %> lignes</p>
+ </div>
+ </div>
+ </li>
<% end %>
-</ul>
+ </ul>
+</div>
-<div class="help-entry" id="help"><%= LSGWeb.LayoutView.liquid_markdown(@conn, @doc) %></div>
+<div class="prose" id="help"><%= LSGWeb.LayoutView.liquid_markdown(@conn, @doc) %></div>
diff --git a/lib/lsg_web/templates/layout/app.html.eex b/lib/lsg_web/templates/layout/app.html.eex
index 1871e8b..9e78e8e 100644
--- a/lib/lsg_web/templates/layout/app.html.eex
+++ b/lib/lsg_web/templates/layout/app.html.eex
@@ -8,28 +8,137 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, noarchive, nofollow, nosnippet" />
<title><%= Map.get(assigns, :title, "") %></title>
- <link rel="stylesheet" href="<%= static_path(@conn, "/assets/css/app.css") %>">
+ <link rel="stylesheet" href="<%= static_path(@conn, "/assets/css/tailwind-ui.min.css") %>">
</head>
<body>
- <div class="container">
- <main role="main">
- <%= if !@conn.assigns[:no_header] do %>
- <h1>
- <small style="font-size: 14px;">
- <a href="/"><%= Keyword.get(Application.get_env(:lsg, :irc), :name, "ircbot") %></a>
- <%= if @conn.assigns[:account], do: @conn.assigns.account.name %>
- &rsaquo;
+<div>
+ <div class="bg-gray-800 pb-32">
+ <nav class="bg-gray-800">
+ <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
+ <div class="border-b border-gray-700">
+ <div class="flex items-center justify-between h-16 px-4 sm:px-0">
+ <div class="flex items-center">
+ <div class="flex-shrink-0">
+ <img class="h-8 w-8" src="https://tailwindui.com/img/logos/workflow-mark-on-dark.svg" alt="Workflow logo">
+ </div>
+ <div class="hidden md:block">
+ <div class="ml-10 flex items-baseline">
+ <a href="/" class="px-3 py-2 rounded-md text-sm font-medium text-white bg-gray-900 focus:outline-none focus:text-white focus:bg-gray-700">bot.goulag.org</a>
+ <!--<a href="#" class="ml-4 px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Team</a>
+ <a href="#" class="ml-4 px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Projects</a>
+ <a href="#" class="ml-4 px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Calendar</a>
+ <a href="#" class="ml-4 px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Reports</a>-->
+ </div>
+ </div>
+ </div>
+ <div class="hidden md:block">
+ <div class="ml-4 flex items-center md:ml-6">
+ <!--<button class="p-1 border-2 border-transparent text-gray-400 rounded-full hover:text-white focus:outline-none focus:text-white focus:bg-gray-700" aria-label="Notifications">
+ <svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
+ </svg>
+ </button>-->
+ <!-- Profile dropdown -->
+ <div class="ml-3 relative">
+ <div>
+ <button class="max-w-xs flex items-center text-sm rounded-full text-white focus:outline-none focus:shadow-solid" id="user-menu" aria-label="User menu" aria-haspopup="true">
+ <!--<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">-->
+ ~<%= if @conn.assigns[:account], do: @conn.assigns.account.name %>
+
+ </button>
+ </div>
+ <!--
+ Profile dropdown panel, show/hide based on dropdown state.
+
+ Entering: "transition ease-out duration-100"
+ From: "transform opacity-0 scale-95"
+ To: "transform opacity-100 scale-100"
+ Leaving: "transition ease-in duration-75"
+ From: "transform opacity-100 scale-100"
+ To: "transform opacity-0 scale-95"
+ -->
+ <!--<div class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
+ <div class="py-1 rounded-md bg-white shadow-xs">
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Your Profile</a>
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a>
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Sign out</a>
+ </div>
+ </div>-->
+ </div>
+ </div>
+ </div>
+ <div class="-mr-2 flex md:hidden">
+ <!-- Mobile menu button -->
+ <button class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:bg-gray-700 focus:text-white">
+ <!-- Menu open: "hidden", Menu closed: "block" -->
+ <svg class="block h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
+ </svg>
+ <!-- Menu open: "block", Menu closed: "hidden" -->
+ <svg class="hidden h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
+ </svg>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!--
+ Mobile menu, toggle classes based on menu state.
+
+ Open: "block", closed: "hidden"
+ -->
+ <div class="hidden border-b border-gray-700 md:hidden">
+ <div class="px-2 py-3 sm:px-3">
+ <a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-white bg-gray-900 focus:outline-none focus:text-white focus:bg-gray-700">Dashboard</a>
+ <a href="#" class="mt-1 block px-3 py-2 rounded-md text-base font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Team</a>
+ <a href="#" class="mt-1 block px-3 py-2 rounded-md text-base font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Projects</a>
+ <a href="#" class="mt-1 block px-3 py-2 rounded-md text-base font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Calendar</a>
+ <a href="#" class="mt-1 block px-3 py-2 rounded-md text-base font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Reports</a>
+ </div>
+ <div class="pt-4 pb-3 border-t border-gray-700">
+ <div class="flex items-center px-5">
+ <div class="flex-shrink-0">
+ <img class="h-10 w-10 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
+ </div>
+ <div class="ml-3">
+ <div class="text-base font-medium leading-none text-white">Tom Cook</div>
+ <div class="mt-1 text-sm font-medium leading-none text-gray-400">tom@example.com</div>
+ </div>
+ </div>
+ <div class="mt-3 px-2" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
+ <a href="#" class="block px-3 py-2 rounded-md text-base font-medium text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700" role="menuitem">Your Profile</a>
+ <a href="#" class="mt-1 block px-3 py-2 rounded-md text-base font-medium text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700" role="menuitem">Settings</a>
+ <a href="#" class="mt-1 block px-3 py-2 rounded-md text-base font-medium text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700" role="menuitem">Sign out</a>
+ </div>
+ </div>
+ </div>
+ </nav>
+ <header class="py-10">
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+ <h1 class="text-3xl leading-9 font-bold text-white">
<%= if n = @conn.assigns[:network] do %><a href="/<%= n %>"><%= n %></a> &rsaquo; <% end %>
<%= if c = @conn.assigns[:chan] do %><a href="/<%= @conn.assigns.network %>/<%= LSGWeb.format_chan(c) %>"><%= c %></a> &rsaquo; <% end %>
<%= for({name, href} <- @conn.assigns[:breadcrumbs]||[], do: [link(name, to: href), raw(" &rsaquo; ")]) %>
- </small><br />
<%= @conn.assigns[:title] || @conn.assigns[:chan] || @conn.assigns[:network] %>
- </h1>
- <% end %>
+ </h1>
+ </div>
+ </header>
+ </div>
+
+ <main class="-mt-32">
+ <div class="max-w-7xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
+ <!-- Replace with your content -->
+ <div class="bg-white rounded-lg shadow px-5 py-6 sm:px-6">
<%= render @view_module, @view_template, assigns %>
- </main>
+ </div>
+ <!-- /End replace -->
</div>
+ </main>
+</div>
+
<script src="<%= static_path(@conn, "/assets/js/app.js") %>"></script>
</body>
</html>
diff --git a/lib/lsg_web/templates/page/user.html.eex b/lib/lsg_web/templates/page/user.html.eex
index 8a043a0..56fc485 100644
--- a/lib/lsg_web/templates/page/user.html.eex
+++ b/lib/lsg_web/templates/page/user.html.eex
@@ -1,3 +1,4 @@
+<div class="prose prose-lg">
<ul>
<li><%= link("Help", to: "/-") %></li>
</ul>
@@ -36,3 +37,4 @@
<li><%= net %>: <%= to_string(predicate) %>, <%= v %></li>
<% end %>
</ul>
+</div>
diff --git a/lib/lsg_web/views/alcoolog_view.ex b/lib/lsg_web/views/alcoolog_view.ex
index 2c55c09..ed3c9b4 100644
--- a/lib/lsg_web/views/alcoolog_view.ex
+++ b/lib/lsg_web/views/alcoolog_view.ex
@@ -1,4 +1,6 @@
defmodule LSGWeb.AlcoologView do
use LSGWeb, :view
+ require Integer
+
end
diff --git a/lib/lsg_web/views/layout_view.ex b/lib/lsg_web/views/layout_view.ex
index b28d3c5..41c5341 100644
--- a/lib/lsg_web/views/layout_view.ex
+++ b/lib/lsg_web/views/layout_view.ex
@@ -60,11 +60,14 @@ defmodule LSGWeb.LayoutView do
now_last_week = {y, w-1}
now_last_roll = 7-Timex.days_to_beginning_of_week(now)
+ date_date = DateTime.to_date(date)
+ now_date = DateTime.to_date(date)
+
format = cond do
date.year != now.year -> "{D}/{M}/{YYYY} {h24}:{m}"
- date.day == now.day -> "{h24}:{m}"
+ date_date == now_date -> "{h24}:{m}"
(now_week == date_week) || (date_week == now_last_week && (Date.day_of_week(date) >= now_last_roll)) -> "{WDfull} {h24}:{m}"
- (now.month == date.month) -> "{WDfull} {D} {h24}:{m}"
+ (now.year == date.year && now.month == date.month) -> "{WDfull} {D} {h24}:{m}"
true -> "{WDfull} {D} {M} {h24}:{m}"
end
diff --git a/lib/tmpl.ex b/lib/tmpl.ex
index cc5ece2..1470603 100644
--- a/lib/tmpl.ex
+++ b/lib/tmpl.ex
@@ -65,6 +65,7 @@ defmodule Tmpl do
{:ok, template_ast} ->
do_render(template_ast, context, safe)
{:error, err, pos} ->
+ Logger.debug("Liquid error: #{pos} - #{inspect template}")
"[liquid ast error (at #{pos}): #{inspect err}]"
end
end
diff --git a/lib/util.ex b/lib/util.ex
index 22d1034..d35157b 100644
--- a/lib/util.ex
+++ b/lib/util.ex
@@ -4,13 +4,15 @@ defmodule Util do
def plusminus(0), do: "0"
def plusminus(number) when number < 0, do: "#{number}"
- def float_paparse(string) do
+ def float_paparse(float) when is_float(float), do: {float, ""}
+ def float_paparse(int) when is_integer(int), do: {(int+0.0), ""}
+ def float_paparse(string) when is_binary(string) do
string
|> String.replace(",", ".")
|> Float.parse()
end
- def ets_mutate_select_each(ets, table, spec, fun) do
+ def ets_mutate_select_each(ets, table, spec \\ [{:"$1", [], [:"$1"]}], fun) do
ets.safe_fixtable(table, true)
first = ets.select(table, spec, 1)
do_ets_mutate_select_each(ets, table, fun, first)