summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/alcool.ex10
-rw-r--r--lib/couch.ex108
-rw-r--r--lib/irc.ex14
-rw-r--r--lib/irc/admin_handler.ex11
-rw-r--r--lib/irc/message.ex13
-rw-r--r--lib/matrix.ex56
-rw-r--r--lib/matrix/plug.ex6
-rw-r--r--lib/matrix/room.ex161
-rw-r--r--lib/nola.ex2
-rw-r--r--lib/nola/account.ex58
-rw-r--r--lib/nola/auth_token.ex21
-rw-r--r--lib/nola/icecast.ex100
-rw-r--r--lib/nola/icecast_agent.ex6
-rw-r--r--lib/nola/membership.ex43
-rw-r--r--lib/nola/message.ex21
-rw-r--r--lib/nola/plugins.ex44
-rw-r--r--lib/nola/subnet.ex67
-rw-r--r--lib/nola/token.ex24
-rw-r--r--lib/nola/trigger.ex1
-rw-r--r--lib/nola/user_track.ex323
-rw-r--r--lib/open_ai.ex23
-rw-r--r--lib/plugins/account.ex326
-rw-r--r--lib/plugins/alcoolog.ex1689
-rw-r--r--lib/plugins/alcoolog_announcer.ex354
-rw-r--r--lib/plugins/base.ex90
-rw-r--r--lib/plugins/buffer.ex16
-rw-r--r--lib/plugins/calc.ex26
-rw-r--r--lib/plugins/coronavirus.ex266
-rw-r--r--lib/plugins/correction.ex30
-rw-r--r--lib/plugins/dice.ex29
-rw-r--r--lib/plugins/finance.ex172
-rw-r--r--lib/plugins/helpers/temp_ref.ex27
-rw-r--r--lib/plugins/kick_roulette.ex7
-rw-r--r--lib/plugins/last_fm.ex167
-rw-r--r--lib/plugins/link/github.ex18
-rw-r--r--lib/plugins/link/image.ex110
-rw-r--r--lib/plugins/link/img_debrid_link.ex16
-rw-r--r--lib/plugins/link/imgur.ex37
-rw-r--r--lib/plugins/link/pdf.ex24
-rw-r--r--lib/plugins/link/redacted.ex6
-rw-r--r--lib/plugins/link/reddit.ex149
-rw-r--r--lib/plugins/link/scraper.ex39
-rw-r--r--lib/plugins/link/store.ex1
-rw-r--r--lib/plugins/link/twitter.ex127
-rw-r--r--lib/plugins/link/youtube.ex53
-rw-r--r--lib/plugins/logger.ex39
-rw-r--r--lib/plugins/quatre_cent_vingt.ex107
-rw-r--r--lib/plugins/radio_france.ex59
-rw-r--r--lib/plugins/say.ex26
-rw-r--r--lib/plugins/script.ex16
-rw-r--r--lib/plugins/seen.ex16
-rw-r--r--lib/plugins/sms.ex121
-rw-r--r--lib/plugins/tell.ex62
-rw-r--r--lib/plugins/txt.ex424
-rw-r--r--lib/plugins/txt/markov.ex8
-rw-r--r--lib/plugins/txt/markov_py_markovify.ex13
-rw-r--r--lib/plugins/untappd.ex55
-rw-r--r--lib/plugins/user_mention.ex30
-rw-r--r--lib/plugins/wikipedia.ex40
-rw-r--r--lib/plugins/wolfram_alpha.ex27
-rw-r--r--lib/plugins/youtube.ex68
-rw-r--r--lib/telegram.ex307
-rw-r--r--lib/telegram/room.ex183
-rw-r--r--lib/tmpl.ex42
-rw-r--r--lib/untappd.ex71
-rw-r--r--lib/util.ex12
-rw-r--r--lib/web.ex15
-rw-r--r--lib/web/channels/user_socket.ex2
-rw-r--r--lib/web/components/component.ex5
-rw-r--r--lib/web/components/event_component.ex2
-rw-r--r--lib/web/components/message_component.ex1
-rw-r--r--lib/web/context_plug.ex108
-rw-r--r--lib/web/controllers/alcoolog_controller.ex418
-rw-r--r--lib/web/controllers/icecast_see_controller.ex12
-rw-r--r--lib/web/controllers/irc_auth_sse_controller.ex56
-rw-r--r--lib/web/controllers/irc_controller.ex124
-rw-r--r--lib/web/controllers/network_controller.ex3
-rw-r--r--lib/web/controllers/open_id_controller.ex39
-rw-r--r--lib/web/controllers/page_controller.ex37
-rw-r--r--lib/web/controllers/sms_controller.ex3
-rw-r--r--lib/web/controllers/untappd_controller.ex8
-rw-r--r--lib/web/live/chat_live.ex109
-rw-r--r--lib/web/router.ex108
-rw-r--r--lib/web/views/alcoolog_view.ex2
-rw-r--r--lib/web/views/error_helpers.ex4
-rw-r--r--lib/web/views/error_view.ex2
-rw-r--r--lib/web/views/layout_view.ex123
-rw-r--r--lib/web/views/network_view.ex1
-rw-r--r--lib/web/views/open_id_view.ex1
89 files changed, 4923 insertions, 2877 deletions
diff --git a/lib/alcool.ex b/lib/alcool.ex
index 40384ba..044e13b 100644
--- a/lib/alcool.ex
+++ b/lib/alcool.ex
@@ -1,16 +1,14 @@
defmodule Alcool do
-
- @spec units(Float.t, Float.t) :: Float.t
+ @spec units(Float.t(), Float.t()) :: Float.t()
def units(cl, degrees) do
- kg(cl, degrees)*100
+ kg(cl, degrees) * 100
end
def grams(cl, degrees) do
- kg(cl, degrees)*1000
+ kg(cl, degrees) * 1000
end
def kg(cl, degrees) do
- (((cl/100) * 0.8) * (degrees/100))
+ cl / 100 * 0.8 * (degrees / 100)
end
-
end
diff --git a/lib/couch.ex b/lib/couch.ex
index 6b39100..8180e6f 100644
--- a/lib/couch.ex
+++ b/lib/couch.ex
@@ -3,17 +3,24 @@ defmodule Couch do
Simple, no-frills, CouchDB client
"""
- @type base_error :: {:error, :bad_request} | {:error, :unauthorized} |
- {:error, :server_error} | {:error, :service_unavailable} |
- {:error, :bad_response} | {:error, HTTPoison.Error.t()}
+ @type base_error ::
+ {:error, :bad_request}
+ | {:error, :unauthorized}
+ | {:error, :server_error}
+ | {:error, :service_unavailable}
+ | {:error, :bad_response}
+ | {:error, HTTPoison.Error.t()}
def new(db, params \\ []) do
{url, headers, options} = prepare_request([db], [], params)
+
case HTTPoison.put(url, headers, options) do
{:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] ->
:ok
+
{:ok, %HTTPoison.Response{status_code: 412}} ->
{:error, :exists}
+
error ->
handle_generic_response(error)
end
@@ -24,48 +31,70 @@ defmodule Couch do
`params` are [documented here](https://docs.couchdb.org/en/3.2.2-docs/api/document/common.html)
"""
- @spec get(String.t(), String.t() | :all_docs, Keyword.t()) :: {:ok, Map.t()} | {:error, :not_found} | {:error, any()}
+ @spec get(String.t(), String.t() | :all_docs, Keyword.t()) ::
+ {:ok, Map.t()} | {:error, :not_found} | {:error, any()}
def get(db, doc, params \\ [])
def get(db, :all_docs, params), do: get(db, "_all_docs", params)
+
def get(db, doc, params) do
{url, headers, options} = prepare_request([db, doc], [], params)
+
case HTTPoison.get(url, headers, options) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, Poison.decode!(body)}
- error -> handle_generic_response(error)
+
+ error ->
+ handle_generic_response(error)
end
end
- @spec post(String.t(), Map.t(), Keyword.t()) :: {:error, :operation_failed} | {:error, :not_found} | {:error, :exists} | {:error, any()}
+ @spec post(String.t(), Map.t(), Keyword.t()) ::
+ {:error, :operation_failed} | {:error, :not_found} | {:error, :exists} | {:error, any()}
def post(db, data, params \\ []) do
- {url, headers, options} = prepare_request([db], [{"content-type", "application/json"}], params)
- with \
- {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] <- HTTPoison.post(url, Poison.encode!(data), headers, options),
- {:json, {:ok, %{"ok" => true, "id" => id, "rev" => rev}}} <- {:json, Poison.decode(body)} do
- {:ok, id, rev}
+ {url, headers, options} =
+ prepare_request([db], [{"content-type", "application/json"}], params)
+
+ with {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] <-
+ HTTPoison.post(url, Poison.encode!(data), headers, options),
+ {:json, {:ok, %{"ok" => true, "id" => id, "rev" => rev}}} <- {:json, Poison.decode(body)} do
+ {:ok, id, rev}
else
- {:ok, %HTTPoison.Response{status_code: 409}} -> {:error, :exists}
+ {:ok, %HTTPoison.Response{status_code: 409}} ->
+ {:error, :exists}
+
{:json, {:ok, body}} ->
- Logger.error("couch: operation failed: #{inspect body}")
+ Logger.error("couch: operation failed: #{inspect(body)}")
{:error, :operation_failed}
- error -> handle_generic_response(error)
+
+ error ->
+ handle_generic_response(error)
end
end
- @spec put(String.t(), Map.t(), Keyword.t()) :: {:error, :operation_failed} | {:error, :not_found} | {:error, :conflict} | {:error, any()}
+ @spec put(String.t(), Map.t(), Keyword.t()) ::
+ {:error, :operation_failed}
+ | {:error, :not_found}
+ | {:error, :conflict}
+ | {:error, any()}
def put(db, doc = %{"_id" => id, "_rev" => _}, params \\ []) do
- {url, headers, options} = prepare_request([db, id], [{"content-type", "application/json"}], params)
+ {url, headers, options} =
+ prepare_request([db, id], [{"content-type", "application/json"}], params)
+
case HTTPoison.put(url, Poison.encode!(doc), headers, options) do
{:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] ->
body = Poison.decode!(body)
+
if Map.get(body, "ok") do
{:ok, Map.get(body, "id"), Map.get(body, "rev")}
else
{:error, :operation_failed}
end
+
{:ok, %HTTPoison.Response{status_code: 209}} ->
{:error, :conflict}
- error -> handle_generic_response(error)
+
+ error ->
+ handle_generic_response(error)
end
end
@@ -74,22 +103,30 @@ defmodule Couch do
base_url = Keyword.get(config, :url, "http://localhost:5984")
- path = path
- |> Enum.filter(& &1)
- |> Enum.map(&to_string/1)
- |> Enum.map(fn(part) -> part
- |> String.replace("/", "%2F")
- |> String.replace("#", "%23")
- end)
- |> Path.join()
-
- url = base_url
- |> URI.merge(path)
- |> to_string()
-
- headers = headers ++ [{"accept", "application/json"}, {"user-agent", "#{Nola.brand(:name)} v#{Nola.version()}"}]
-
- params = Enum.map(params, fn({k, v}) -> {to_string(k), v} end)
+ path =
+ path
+ |> Enum.filter(& &1)
+ |> Enum.map(&to_string/1)
+ |> Enum.map(fn part ->
+ part
+ |> String.replace("/", "%2F")
+ |> String.replace("#", "%23")
+ end)
+ |> Path.join()
+
+ url =
+ base_url
+ |> URI.merge(path)
+ |> to_string()
+
+ headers =
+ headers ++
+ [
+ {"accept", "application/json"},
+ {"user-agent", "#{Nola.brand(:name)} v#{Nola.version()}"}
+ ]
+
+ params = Enum.map(params, fn {k, v} -> {to_string(k), v} end)
client_options = Keyword.get(config, :client_options, [])
options = [params: params] ++ options ++ client_options
@@ -102,7 +139,8 @@ defmodule Couch do
{url, headers, options}
end
- defp handle_generic_response({:ok, %HTTPoison.Response{status_code: code}}), do: {:error, Plug.Conn.Status.reason_atom(code)}
- defp handle_generic_response({:error, %HTTPoison.Error{reason: reason}}), do: {:error, reason}
+ defp handle_generic_response({:ok, %HTTPoison.Response{status_code: code}}),
+ do: {:error, Plug.Conn.Status.reason_atom(code)}
+ defp handle_generic_response({:error, %HTTPoison.Error{reason: reason}}), do: {:error, reason}
end
diff --git a/lib/irc.ex b/lib/irc.ex
index 2c7a468..26e3843 100644
--- a/lib/irc.ex
+++ b/lib/irc.ex
@@ -6,6 +6,7 @@ defmodule Nola.Irc do
def send_message_as(account, network, channel, text, force_puppet \\ false, meta \\ []) do
connection = Nola.Irc.Connection.get_network(network)
+
if connection && (force_puppet || Nola.Irc.PuppetConnection.whereis(account, connection)) do
Nola.Irc.PuppetConnection.start_and_send_message(account, connection, channel, text, meta)
else
@@ -21,7 +22,7 @@ defmodule Nola.Irc do
for {n, u, h} <- Nola.Irc.env(:admins, []) do
admin_part_match?(n, nick) && admin_part_match?(u, user) && admin_part_match?(h, host)
end
- |> Enum.any?
+ |> Enum.any?()
end
defp admin_part_match?(:_, _), do: true
@@ -34,9 +35,13 @@ defmodule Nola.Irc do
Nola.Irc.Connection.setup()
[
- worker(Registry, [[keys: :duplicate, name: Nola.Irc.ConnectionPubSub]], id: :registr_irc_conn),
- supervisor(Nola.Irc.Connection.Supervisor, [], [name: Nola.Irc.Connection.Supervisor]),
- supervisor(Nola.Irc.PuppetConnection.Supervisor, [], [name: Nola.Irc.PuppetConnection.Supervisor]),
+ worker(Registry, [[keys: :duplicate, name: Nola.Irc.ConnectionPubSub]],
+ id: :registr_irc_conn
+ ),
+ supervisor(Nola.Irc.Connection.Supervisor, [], name: Nola.Irc.Connection.Supervisor),
+ supervisor(Nola.Irc.PuppetConnection.Supervisor, [],
+ name: Nola.Irc.PuppetConnection.Supervisor
+ )
]
end
@@ -45,5 +50,4 @@ defmodule Nola.Irc do
Logger.info("Starting connections")
Nola.Irc.Connection.start_all()
end
-
end
diff --git a/lib/irc/admin_handler.ex b/lib/irc/admin_handler.ex
index 39556fe..e4834fa 100644
--- a/lib/irc/admin_handler.ex
+++ b/lib/irc/admin_handler.ex
@@ -13,17 +13,22 @@ defmodule Nola.Irc.AdminHandler do
end
def init([client]) do
- ExIRC.Client.add_handler client, self()
+ ExIRC.Client.add_handler(client, self())
{:ok, _} = Registry.register(Nola.PubSub, "op", [])
{:ok, client}
end
- def handle_info({:irc, :trigger, "op", m = %Nola.Message{trigger: %Nola.Trigger{type: :bang}, sender: sender}}, client) do
+ def handle_info(
+ {:irc, :trigger, "op",
+ m = %Nola.Message{trigger: %Nola.Trigger{type: :bang}, sender: sender}},
+ client
+ ) do
if Nola.Irc.admin?(sender) do
m.replyfun.({:mode, "+o"})
else
m.replyfun.({:kick, "non"})
end
+
{:noreply, client}
end
@@ -31,11 +36,11 @@ defmodule Nola.Irc.AdminHandler do
if Nola.Irc.admin?(sender) do
ExIRC.Client.mode(client, chan, "+o", sender.nick)
end
+
{:noreply, client}
end
def handle_info(msg, client) do
{:noreply, client}
end
-
end
diff --git a/lib/irc/message.ex b/lib/irc/message.ex
index 3927079..4b61d36 100644
--- a/lib/irc/message.ex
+++ b/lib/irc/message.ex
@@ -1,28 +1,27 @@
defmodule Nola.Irc.Message do
-
@max_chars 440
def splitlong(string, max_chars \\ 440)
def splitlong(string, max_chars) when is_list(string) do
- Enum.map(string, fn(s) -> splitlong(s, max_chars) end)
+ Enum.map(string, fn s -> splitlong(s, max_chars) end)
|> List.flatten()
end
def splitlong(string, max_chars) do
string
- |> String.codepoints
+ |> String.codepoints()
|> Enum.chunk_every(max_chars)
|> Enum.map(&Enum.join/1)
end
def splitlong_with_prefix(string, prefix, max_chars \\ 440) do
prefix = "#{prefix} "
- max_chars = max_chars - (length(String.codepoints(prefix)))
+ max_chars = max_chars - length(String.codepoints(prefix))
+
string
- |> String.codepoints
+ |> String.codepoints()
|> Enum.chunk_every(max_chars)
- |> Enum.map(fn(line) -> prefix <> Enum.join(line) end)
+ |> Enum.map(fn line -> prefix <> Enum.join(line) end)
end
-
end
diff --git a/lib/matrix.ex b/lib/matrix.ex
index 0ad0836..384f204 100644
--- a/lib/matrix.ex
+++ b/lib/matrix.ex
@@ -5,7 +5,7 @@ defmodule Nola.Matrix do
@behaviour MatrixAppService.Adapter.Room
@behaviour MatrixAppService.Adapter.Transaction
@behaviour MatrixAppService.Adapter.User
- @env Mix.env
+ @env Mix.env()
def dets(part) do
(Nola.data_path() <> "/matrix-#{to_string(part)}.dets") |> String.to_charlist()
@@ -20,12 +20,13 @@ defmodule Nola.Matrix do
def myself?("@_dev:random.sh"), do: true
def myself?("@_bot:random.sh"), do: true
- def myself?("@_dev."<>_), do: true
- def myself?("@_bot."<>_), do: true
+ def myself?("@_dev." <> _), do: true
+ def myself?("@_bot." <> _), do: true
def myself?(_), do: false
- def mxc_to_http(mxc = "mxc://"<>_) do
+ def mxc_to_http(mxc = "mxc://" <> _) do
uri = URI.parse(mxc)
+
%URI{uri | scheme: "https", path: "/_matrix/media/r0/download/#{uri.authority}#{uri.path}"}
|> URI.to_string()
end
@@ -41,6 +42,7 @@ defmodule Nola.Matrix do
initial_device_display_name: "Application Service",
username: if(@env == :dev, do: "_dev.#{id}", else: "_bot.#{id}")
]
+
Logger.debug("Registering user for #{id}")
{:ok, %{"user_id" => mxid}} = Polyjuice.Client.LowLevel.register(client(), opts)
:dets.insert(dets(:users), {id, mxid})
@@ -54,27 +56,31 @@ defmodule Nola.Matrix do
end
end
- def user_name("@"<>name) do
+ def user_name("@" <> name) do
[username, _] = String.split(name, ":", parts: 2)
username
end
def application_childs() do
import Supervisor.Spec
+
[
- supervisor(Nola.Matrix.Room.Supervisor, [], [name: Nola.Irc.PuppetConnection.Supervisor]),
+ supervisor(Nola.Matrix.Room.Supervisor, [], name: Nola.Irc.PuppetConnection.Supervisor)
]
end
def after_start() do
- rooms = :dets.foldl(fn({id, _, _, _}, acc) -> [id | acc] end, [], dets(:rooms))
+ rooms = :dets.foldl(fn {id, _, _, _}, acc -> [id | acc] end, [], dets(:rooms))
for room <- rooms, do: Nola.Matrix.Room.start(room)
end
def lookup_room(room) do
case :dets.lookup(dets(:rooms), room) do
- [{_, network, channel, opts}] -> {:ok, Map.merge(opts, %{network: network, channel: channel})}
- _ -> {:error, :no_such_room}
+ [{_, network, channel, opts}] ->
+ {:ok, Map.merge(opts, %{network: network, channel: channel})}
+
+ _ ->
+ {:error, :no_such_room}
end
end
@@ -93,11 +99,16 @@ defmodule Nola.Matrix do
end
def create_room(room_alias) do
- Logger.debug("Matrix: creating room #{inspect room_alias}")
+ Logger.debug("Matrix: creating room #{inspect(room_alias)}")
localpart = localpart(room_alias)
+
with {:ok, network, channel} <- extract_network_channel_from_localpart(localpart),
%Nola.Irc.Connection{} <- Nola.Irc.Connection.get_network(network, channel),
- room = [visibility: :public, room_alias_name: localpart, name: if(network == "random", do: channel, else: "#{network}/#{channel}")],
+ room = [
+ visibility: :public,
+ room_alias_name: localpart,
+ name: if(network == "random", do: channel, else: "#{network}/#{channel}")
+ ],
{:ok, %{"room_id" => room_id}} <- Client.Room.create_room(client(), room) do
Logger.info("Matrix: created room #{room_alias} #{room_id}")
:dets.insert(dets(:rooms), {room_id, network, channel, %{}})
@@ -110,14 +121,15 @@ defmodule Nola.Matrix do
end
def localpart(room_alias) do
- [<<"#", localpart :: binary>>, _] = String.split(room_alias, ":", parts: 2)
+ [<<"#", localpart::binary>>, _] = String.split(room_alias, ":", parts: 2)
localpart
end
def extract_network_channel_from_localpart(localpart) do
- s = localpart
- |> String.replace("dev.", "")
- |> String.split("/", parts: 2)
+ s =
+ localpart
+ |> String.replace("dev.", "")
+ |> String.split("/", parts: 2)
case s do
[network, channel] -> {:ok, network, channel}
@@ -132,38 +144,42 @@ defmodule Nola.Matrix do
{:ok, room_id} ->
Nola.Matrix.Room.start(room_id)
:ok
- error -> error
+
+ error ->
+ error
end
end
@impl MatrixAppService.Adapter.Transaction
def new_event(event = %MatrixAppService.Event{}) do
- Logger.debug("New matrix event: #{inspect event}")
+ Logger.debug("New matrix event: #{inspect(event)}")
+
if event.room_id do
Nola.Matrix.Room.start_and_send_matrix_event(event.room_id, event)
end
+
:noop
end
@impl MatrixAppService.Adapter.User
def query_user(user_id) do
- Logger.warn("Matrix lookup user: #{inspect user_id}")
+ Logger.warn("Matrix lookup user: #{inspect(user_id)}")
:error
end
def client(opts \\ []) do
base_url = Application.get_env(:matrix_app_service, :base_url)
access_token = Application.get_env(:matrix_app_service, :access_token)
+
default_opts = [
access_token: access_token,
device_id: "APP_SERVICE",
application_service: true,
user_id: nil
]
+
opts = Keyword.merge(default_opts, opts)
Polyjuice.Client.LowLevel.create(base_url, opts)
end
-
-
end
diff --git a/lib/matrix/plug.ex b/lib/matrix/plug.ex
index c64ed11..e9be4ad 100644
--- a/lib/matrix/plug.ex
+++ b/lib/matrix/plug.ex
@@ -1,11 +1,10 @@
defmodule Nola.Matrix.Plug do
-
defmodule Auth do
- def init(state) do
+ def init(state) do
state
end
- def call(conn, _) do
+ def call(conn, _) do
hs = Application.get_env(:matrix_app_service, :homeserver_token)
MatrixAppServiceWeb.AuthPlug.call(conn, hs)
end
@@ -21,5 +20,4 @@ defmodule Nola.Matrix.Plug do
MatrixAppServiceWeb.SetConfigPlug.call(conn, config)
end
end
-
end
diff --git a/lib/matrix/room.ex b/lib/matrix/room.ex
index e2965a5..b3921f6 100644
--- a/lib/matrix/room.ex
+++ b/lib/matrix/room.ex
@@ -12,7 +12,12 @@ defmodule Nola.Matrix.Room do
end
def start_child(room_id) do
- spec = %{id: room_id, start: {Nola.Matrix.Room, :start_link, [room_id]}, restart: :transient}
+ spec = %{
+ id: room_id,
+ start: {Nola.Matrix.Room, :start_link, [room_id]},
+ restart: :transient
+ }
+
DynamicSupervisor.start_child(__MODULE__, spec)
end
@@ -35,20 +40,23 @@ defmodule Nola.Matrix.Room do
end
def start_and_send_matrix_event(room_id, event) do
- pid = if pid = whereis(room_id) do
- pid
- else
- case __MODULE__.start(room_id) do
- {:ok, pid} -> pid
- {:error, {:already_started, pid}} -> pid
- :ignore -> nil
+ pid =
+ if pid = whereis(room_id) do
+ pid
+ else
+ case __MODULE__.start(room_id) do
+ {:ok, pid} -> pid
+ {:error, {:already_started, pid}} -> pid
+ :ignore -> nil
+ end
end
- end
+
if(pid, do: send(pid, {:matrix_event, event}))
end
def whereis(room_id) do
{:global, name} = name(room_id)
+
case :global.whereis_name(name) do
:undefined -> nil
pid -> pid
@@ -65,45 +73,62 @@ defmodule Nola.Matrix.Room do
Logger.metadata(matrix_room: room_id)
{:ok, _} = Registry.register(Nola.PubSub, "#{state.network}:events", plugin: __MODULE__)
+
for t <- ["messages", "triggers", "outputs", "events"] do
- {:ok, _} = Registry.register(Nola.PubSub, "#{state.network}/#{state.channel}:#{t}", plugin: __MODULE__)
+ {:ok, _} =
+ Registry.register(Nola.PubSub, "#{state.network}/#{state.channel}:#{t}",
+ plugin: __MODULE__
+ )
end
- state = state
- |> Map.put(:id, room_id)
+ state =
+ state
+ |> Map.put(:id, room_id)
+
Logger.info("Started Matrix room #{room_id}")
{:ok, state, {:continue, :update_state}}
+
error ->
- Logger.info("Received event for nonexistent room #{inspect room_id}: #{inspect error}")
+ Logger.info("Received event for nonexistent room #{inspect(room_id)}: #{inspect(error)}")
:ignore
end
end
def handle_continue(:update_state, state) do
{:ok, s} = Client.Room.get_state(client(), state.id)
- members = Enum.reduce(s, [], fn(s, acc) ->
- if s["type"] == "m.room.member" do
- if s["content"]["membership"] == "join" do
- [s["user_id"] | acc]
+
+ members =
+ Enum.reduce(s, [], fn s, acc ->
+ if s["type"] == "m.room.member" do
+ if s["content"]["membership"] == "join" do
+ [s["user_id"] | acc]
+ else
+ # XXX: The user left, remove from Nola.Memberships ?
+ acc
+ end
else
- # XXX: The user left, remove from Nola.Memberships ?
acc
end
- else
- acc
- end
- end)
- |> Enum.filter(& &1)
-
- for m <- members, do: Nola.UserTrack.joined(state.id, %{network: "matrix", nick: m, user: m, host: "matrix."}, [], true)
-
- accounts = Nola.UserTrack.channel(state.network, state.channel)
- |> Enum.filter(& &1)
- |> Enum.map(fn(tuple) -> Nola.UserTrack.User.from_tuple(tuple).account end)
- |> Enum.uniq()
- |> Enum.each(fn(account_id) ->
- introduce_irc_account(account_id, state)
- end)
+ end)
+ |> Enum.filter(& &1)
+
+ for m <- members,
+ do:
+ Nola.UserTrack.joined(
+ state.id,
+ %{network: "matrix", nick: m, user: m, host: "matrix."},
+ [],
+ true
+ )
+
+ accounts =
+ Nola.UserTrack.channel(state.network, state.channel)
+ |> Enum.filter(& &1)
+ |> Enum.map(fn tuple -> Nola.UserTrack.User.from_tuple(tuple).account end)
+ |> Enum.uniq()
+ |> Enum.each(fn account_id ->
+ introduce_irc_account(account_id, state)
+ end)
{:noreply, state}
end
@@ -112,6 +137,7 @@ defmodule Nola.Matrix.Room do
def handle_info({:irc, :out, message}, state), do: handle_irc(message, state)
def handle_info({:irc, :trigger, _, message}, state), do: handle_irc(message, state)
def handle_info({:irc, :event, event}, state), do: handle_irc(event, state)
+
def handle_info({:matrix_event, event}, state) do
if myself?(event.user_id) do
{:noreply, state}
@@ -122,14 +148,17 @@ defmodule Nola.Matrix.Room do
def handle_irc(message = %Nola.Message{account: account}, state) do
unless Map.get(message.meta, :puppet) && Map.get(message.meta, :from) == self() do
- opts = if Map.get(message.meta, :self) || is_nil(account) do
- []
- else
- mxid = Matrix.get_or_create_matrix_user(account.id)
- [user_id: mxid]
- end
- Client.Room.send_message(client(opts),state.id, message.text)
+ opts =
+ if Map.get(message.meta, :self) || is_nil(account) do
+ []
+ else
+ mxid = Matrix.get_or_create_matrix_user(account.id)
+ [user_id: mxid]
+ end
+
+ Client.Room.send_message(client(opts), state.id, message.text)
end
+
{:noreply, state}
end
@@ -138,36 +167,56 @@ defmodule Nola.Matrix.Room do
{:noreply, state}
end
- def handle_irc(%{type: quit_or_part, account_id: account_id}, state) when quit_or_part in [:quit, :part] do
+ def handle_irc(%{type: quit_or_part, account_id: account_id}, state)
+ when quit_or_part in [:quit, :part] do
mxid = Matrix.get_or_create_matrix_user(account_id)
Client.Room.leave(client(user_id: mxid), state.id)
{:noreply, state}
end
-
def handle_irc(event, state) do
- Logger.warn("Skipped irc event #{inspect event}")
+ Logger.warn("Skipped irc event #{inspect(event)}")
{:noreply, state}
end
- def handle_matrix(event = %{type: "m.room.member", user_id: user_id, content: %{"membership" => "join"}}, state) do
+ def handle_matrix(
+ event = %{type: "m.room.member", user_id: user_id, content: %{"membership" => "join"}},
+ state
+ ) do
_account = get_account(event, state)
- Nola.UserTrack.joined(state.id, %{network: "matrix", nick: user_id, user: user_id, host: "matrix."}, [], true)
+
+ Nola.UserTrack.joined(
+ state.id,
+ %{network: "matrix", nick: user_id, user: user_id, host: "matrix."},
+ [],
+ true
+ )
+
{:noreply, state}
end
- def handle_matrix(event = %{type: "m.room.member", user_id: user_id, content: %{"membership" => "leave"}}, state) do
+ def handle_matrix(
+ event = %{type: "m.room.member", user_id: user_id, content: %{"membership" => "leave"}},
+ state
+ ) do
Nola.UserTrack.parted(state.id, %{network: "matrix", nick: user_id})
{:noreply, state}
end
- def handle_matrix(event = %{type: "m.room.message", user_id: user_id, content: %{"msgtype" => "m.text", "body" => text}}, state) do
+ def handle_matrix(
+ event = %{
+ type: "m.room.message",
+ user_id: user_id,
+ content: %{"msgtype" => "m.text", "body" => text}
+ },
+ state
+ ) do
Nola.Irc.send_message_as(get_account(event, state), state.network, state.channel, text, true)
{:noreply, state}
end
def handle_matrix(event, state) do
- Logger.warn("Skipped matrix event #{inspect event}")
+ Logger.warn("Skipped matrix event #{inspect(event)}")
{:noreply, state}
end
@@ -180,17 +229,23 @@ defmodule Nola.Matrix.Room do
account = Nola.Account.get(account_id)
user = Nola.UserTrack.find_by_account(state.network, account)
base_nick = if(user, do: user.nick, else: account.name)
+
case Client.Profile.put_displayname(client(user_id: mxid), base_nick) do
- :ok -> :ok
+ :ok ->
+ :ok
+
error ->
- Logger.warn("Failed to update profile for #{mxid}: #{inspect error}")
+ Logger.warn("Failed to update profile for #{mxid}: #{inspect(error)}")
end
+
case Client.Room.join(client(user_id: mxid), state.id) do
- {:ok, _} -> :ok
+ {:ok, _} ->
+ :ok
+
error ->
- Logger.warn("Failed to join room for #{mxid}: #{inspect error}")
+ Logger.warn("Failed to join room for #{mxid}: #{inspect(error)}")
end
+
:ok
end
-
end
diff --git a/lib/nola.ex b/lib/nola.ex
index 51c2150..a9c7287 100644
--- a/lib/nola.ex
+++ b/lib/nola.ex
@@ -1,5 +1,4 @@
defmodule Nola do
-
@default_brand [
name: "Nola",
source_url: "https://phab.random.sh/source/Nola/",
@@ -26,5 +25,4 @@ defmodule Nola do
def version do
Application.spec(:nola)[:vsn]
end
-
end
diff --git a/lib/nola/account.ex b/lib/nola/account.ex
index 70e9e40..d850a82 100644
--- a/lib/nola/account.ex
+++ b/lib/nola/account.ex
@@ -44,7 +44,7 @@ defmodule Nola.Account do
end
def start_link() do
- GenServer.start_link(__MODULE__, [], [name: __MODULE__])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
@@ -63,6 +63,7 @@ defmodule Nola.Account do
def get_by_name(name) do
spec = [{{:_, :"$1", :_}, [{:==, :"$1", {:const, name}}], [:"$_"]}]
+
case :dets.select(file("db"), spec) do
[account] -> from_tuple(account)
_ -> nil
@@ -71,7 +72,7 @@ defmodule Nola.Account do
def get_meta(%__MODULE__{id: id}, key, default \\ nil) do
case :dets.lookup(file("meta"), {id, key}) do
- [{_, value}] -> (value || default)
+ [{_, value}] -> value || default
_ -> default
end
end
@@ -86,8 +87,12 @@ defmodule Nola.Account do
@doc "Find an account given a specific meta `key` and `value`."
@spec find_meta_account(String.t(), String.t()) :: t() | nil
def find_meta_account(key, value) do
- #spec = [{{{:"$1", :"$2"}, :"$3"}, [:andalso, {:==, :"$2", {:const, key}}, {:==, :"$3", {:const, value}}], [:"$1"]}]
- spec = [{{{:"$1", :"$2"}, :"$3"}, [{:andalso, {:==, :"$2", {:const, key}}, {:==, {:const, value}, :"$3"}}], [:"$1"]}]
+ # spec = [{{{:"$1", :"$2"}, :"$3"}, [:andalso, {:==, :"$2", {:const, key}}, {:==, :"$3", {:const, value}}], [:"$1"]}]
+ spec = [
+ {{{:"$1", :"$2"}, :"$3"},
+ [{:andalso, {:==, :"$2", {:const, key}}, {:==, {:const, value}, :"$3"}}], [:"$1"]}
+ ]
+
case :dets.select(file("meta"), spec) do
[id] -> get(id)
_ -> nil
@@ -100,7 +105,7 @@ defmodule Nola.Account do
end
def put_user_meta(account = %__MODULE__{}, key, value) do
- put_meta(account, "u:"<>key, value)
+ put_meta(account, "u:" <> key, value)
end
def put_meta(%__MODULE__{id: id}, key, value) do
@@ -112,15 +117,15 @@ defmodule Nola.Account do
end
def all_accounts() do
- :dets.traverse(file("db"), fn(obj) -> {:continue, from_tuple(obj)} end)
+ :dets.traverse(file("db"), fn obj -> {:continue, from_tuple(obj)} end)
end
def all_predicates() do
- :dets.traverse(file("predicates"), fn(obj) -> {:continue, obj} end)
+ :dets.traverse(file("predicates"), fn obj -> {:continue, obj} end)
end
def all_meta() do
- :dets.traverse(file("meta"), fn(obj) -> {:continue, obj} end)
+ :dets.traverse(file("meta"), fn obj -> {:continue, obj} end)
end
def merge_account(old_id, new_id) do
@@ -130,16 +135,19 @@ defmodule Nola.Account do
for pred <- predicates, do: :ok = :dets.insert(file("predicates"), {pred, new_id})
spec = [{{{:"$1", :"$2"}, :"$3"}, [{:==, :"$1", {:const, old_id}}], [{{:"$2", :"$3"}}]}]
metas = :dets.select(file("meta"), spec)
- for {k,v} <- metas do
+
+ for {k, v} <- metas do
:dets.delete(file("meta"), {{old_id, k}})
:ok = :dets.insert(file("meta"), {{new_id, k}, v})
end
+
:dets.delete(file("db"), old_id)
Nola.Membership.merge_account(old_id, new_id)
Nola.UserTrack.merge_account(old_id, new_id)
Nola.Irc.Connection.dispatch("account", {:account_change, old_id, new_id})
Nola.Irc.Connection.dispatch("conn", {:account_change, old_id, new_id})
end
+
:ok
end
@@ -150,16 +158,15 @@ defmodule Nola.Account do
@doc "Always find an account by nickname, even if offline. Uses predicates and then account name."
def find_always_by_nick(network, chan, nick) do
- with \
- nil <- find_by_nick(network, nick),
+ with nil <- find_by_nick(network, nick),
nil <- do_lookup(%User{network: network, nick: nick}, false),
- nil <- get_by_name(nick)
- do
+ nil <- get_by_name(nick) do
nil
else
%__MODULE__{} = account ->
memberships = Nola.Membership.of_account(account)
- if Enum.any?(memberships, fn({net, ch}) -> (net == network) or (chan && chan == ch) end) do
+
+ if Enum.any?(memberships, fn {net, ch} -> net == network or (chan && chan == ch) end) do
account
else
nil
@@ -173,9 +180,14 @@ defmodule Nola.Account do
def lookup(something, make_default \\ true) do
account = do_lookup(something, make_default)
+
if account && Map.get(something, :nick) do
- Nola.Irc.Connection.dispatch("account", {:account_auth, Map.get(something, :nick), account.id})
+ Nola.Irc.Connection.dispatch(
+ "account",
+ {:account_auth, Map.get(something, :nick), account.id}
+ )
end
+
account
end
@@ -198,7 +210,8 @@ defmodule Nola.Account do
end
end
- defp do_lookup(message = %Nola.Message{account: account_id}, make_default) when is_binary(account_id) do
+ defp do_lookup(message = %Nola.Message{account: account_id}, make_default)
+ when is_binary(account_id) do
get(account_id)
end
@@ -206,8 +219,12 @@ defmodule Nola.Account do
if user = Nola.UserTrack.find_by_nick(sender) do
lookup(user, make_default)
else
- #FIXME this will never work with continued lookup by other methods as Who isn't compatible
- lookup_by_nick(sender, :dets.lookup(file("predicates"), {sender.network,{:nick, sender.nick}}), make_default)
+ # FIXME this will never work with continued lookup by other methods as Who isn't compatible
+ lookup_by_nick(
+ sender,
+ :dets.lookup(file("predicates"), {sender.network, {:nick, sender.nick}}),
+ make_default
+ )
end
end
@@ -220,7 +237,7 @@ defmodule Nola.Account do
end
defp do_lookup(user = %User{network: server, nick: nick}, make_default) do
- lookup_by_nick(user, :dets.lookup(file("predicates"), {server,{:nick, nick}}), make_default)
+ lookup_by_nick(user, :dets.lookup(file("predicates"), {server, {:nick, nick}}), make_default)
end
defp do_lookup(nil, _) do
@@ -232,7 +249,7 @@ defmodule Nola.Account do
end
defp lookup_by_nick(user, _, make_default) do
- #authenticate_by_host(user)
+ # authenticate_by_host(user)
if make_default, do: new_account(user), else: nil
end
@@ -259,5 +276,4 @@ defmodule Nola.Account do
spec = [{{:"$1", :"$2"}, [{:==, :"$2", {:const, account.id}}], [:"$1"]}]
:dets.select(file("predicates"), spec)
end
-
end
diff --git a/lib/nola/auth_token.ex b/lib/nola/auth_token.ex
index 9760ec7..0da4aab 100644
--- a/lib/nola/auth_token.ex
+++ b/lib/nola/auth_token.ex
@@ -2,7 +2,7 @@ defmodule Nola.AuthToken do
use GenServer
def start_link() do
- GenServer.start_link(__MODULE__, [], [name: __MODULE__])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def lookup(id) do
@@ -13,6 +13,7 @@ defmodule Nola.AuthToken do
case new(account, perks) do
{:ok, id} ->
NolaWeb.Router.Helpers.login_path(NolaWeb.Endpoint, :token, id)
+
error ->
error
end
@@ -22,6 +23,7 @@ defmodule Nola.AuthToken do
case new(account, perks) do
{:ok, id} ->
NolaWeb.Router.Helpers.login_url(NolaWeb.Endpoint, :token, id)
+
error ->
error
end
@@ -32,15 +34,14 @@ defmodule Nola.AuthToken do
end
def init(_) do
- {:ok, Map.new}
+ {:ok, Map.new()}
end
def handle_call({:lookup, id}, _, state) do
IO.inspect(state)
- with \
- {account, date, perks} <- Map.get(state, id),
- d when d > 0 <- DateTime.diff(date, DateTime.utc_now())
- do
+
+ with {account, date, perks} <- Map.get(state, id),
+ d when d > 0 <- DateTime.diff(date, DateTime.utc_now()) do
{:reply, {:ok, account, perks}, Map.delete(state, id)}
else
x ->
@@ -51,9 +52,11 @@ defmodule Nola.AuthToken do
def handle_call({:new, account, perks}, _, state) do
id = Nola.UserTrack.Id.token()
- expire = DateTime.utc_now()
- |> DateTime.add(15*60, :second)
+
+ expire =
+ DateTime.utc_now()
+ |> DateTime.add(15 * 60, :second)
+
{:reply, {:ok, id}, Map.put(state, id, {account, expire, perks})}
end
-
end
diff --git a/lib/nola/icecast.ex b/lib/nola/icecast.ex
index 5a53192..021af0b 100644
--- a/lib/nola/icecast.ex
+++ b/lib/nola/icecast.ex
@@ -23,69 +23,82 @@ defmodule Nola.Icecast do
end
defp poll(state) do
- state = case request(base_url(), :get) do
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
- #update_json_stats(Jason.decode(body))
- stats = update_stats(body)
- if state != stats do
- Logger.info "Icecast Update: " <> inspect(stats)
- Nola.IcecastAgent.update(stats)
- Registry.dispatch(Nola.BroadcastRegistry, "icecast", fn ws ->
- for {pid, _} <- ws, do: send(pid, {:icecast, stats})
- end)
- stats
- else
+ state =
+ case request(base_url(), :get) do
+ {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
+ # update_json_stats(Jason.decode(body))
+ stats = update_stats(body)
+
+ if state != stats do
+ Logger.info("Icecast Update: " <> inspect(stats))
+ Nola.IcecastAgent.update(stats)
+
+ Registry.dispatch(Nola.BroadcastRegistry, "icecast", fn ws ->
+ for {pid, _} <- ws, do: send(pid, {:icecast, stats})
+ end)
+
+ stats
+ else
+ state
+ end
+
+ error ->
+ Logger.error("Icecast HTTP Error: #{inspect(error)}")
state
- end
- error ->
- Logger.error "Icecast HTTP Error: #{inspect error}"
- state
- end
+ end
+
interval = Application.get_env(:nola, :icecast_poll_interval, 60_000)
:timer.send_after(interval, :poll)
state
end
defp update_stats(html) do
- raw = Floki.find(html, "div.roundbox")
- |> Enum.map(fn(html) ->
- html = Floki.raw_html(html)
- [{"h3", _, ["Mount Point /"<>mount]}] = Floki.find(html, "h3.mount")
- stats = Floki.find(html, "tr")
- |> Enum.map(fn({"tr", _, tds}) ->
- [{"td", _, keys}, {"td", _, values}] = tds
- key = List.first(keys)
- value = List.first(values)
- {key, value}
+ raw =
+ Floki.find(html, "div.roundbox")
+ |> Enum.map(fn html ->
+ html = Floki.raw_html(html)
+ [{"h3", _, ["Mount Point /" <> mount]}] = Floki.find(html, "h3.mount")
+
+ stats =
+ Floki.find(html, "tr")
+ |> Enum.map(fn {"tr", _, tds} ->
+ [{"td", _, keys}, {"td", _, values}] = tds
+ key = List.first(keys)
+ value = List.first(values)
+ {key, value}
+ end)
+ |> Enum.into(Map.new())
+
+ {mount, stats}
end)
- |> Enum.into(Map.new)
- {mount, stats}
- end)
- |> Enum.into(Map.new)
+ |> Enum.into(Map.new())
live? = if Map.get(raw["live"], "Content Type:", false), do: true, else: false
- np = if live? do
- raw["live"]["Currently playing:"]
- else
- raw["autodj"]["Currently playing:"]
- end
+
+ np =
+ if live? do
+ raw["live"]["Currently playing:"]
+ else
+ raw["autodj"]["Currently playing:"]
+ end
genre = raw["live"]["Genre:"] || nil
%{np: np || "", live: live? || false, genre: genre}
end
defp update_json_stats({:ok, body}) do
- Logger.debug "JSON STATS: #{inspect body}"
+ Logger.debug("JSON STATS: #{inspect(body)}")
end
defp update_json_stats(error) do
- Logger.error "Failed to decode JSON Stats: #{inspect error}"
+ Logger.error("Failed to decode JSON Stats: #{inspect(error)}")
end
defp request(uri, method, body \\ [], headers \\ []) do
headers = [{"user-agent", "Nola-API[115ans.net, sys.115ans.net] href@random.sh"}] ++ headers
options = @httpoison_opts
- case :ok do #:fuse.ask(@fuse, :sync) do
+ # :fuse.ask(@fuse, :sync) do
+ case :ok do
:ok -> run_request(method, uri, body, headers, options)
:blown -> :blown
end
@@ -93,14 +106,18 @@ defmodule Nola.Icecast do
# This is to work around hackney's behaviour of returning `{:error, :closed}` when a pool connection has been closed
# (keep-alive expired). We just retry the request immediatly up to five times.
- defp run_request(method, uri, body, headers, options), do: run_request(method, uri, body, headers, options, 0)
+ defp run_request(method, uri, body, headers, options),
+ do: run_request(method, uri, body, headers, options, 0)
+
defp run_request(method, uri, body, headers, options, retries) when retries < 4 do
case HTTPoison.request(method, uri, body, headers, options) do
{:error, :closed} -> run_request(method, uri, body, headers, options, retries + 1)
other -> other
end
end
- defp run_request(method, uri, body, headers, options, _exceeded_retries), do: {:error, :unavailable}
+
+ defp run_request(method, uri, body, headers, options, _exceeded_retries),
+ do: {:error, :unavailable}
#
# -- URIs
@@ -113,5 +130,4 @@ defmodule Nola.Icecast do
defp base_url do
"http://91.121.59.45:8089"
end
-
end
diff --git a/lib/nola/icecast_agent.ex b/lib/nola/icecast_agent.ex
index 8a3a72b..563b372 100644
--- a/lib/nola/icecast_agent.ex
+++ b/lib/nola/icecast_agent.ex
@@ -6,12 +6,10 @@ defmodule Nola.IcecastAgent do
end
def update(stats) do
- Agent.update(__MODULE__, fn(_old) -> stats end)
+ Agent.update(__MODULE__, fn _old -> stats end)
end
def get do
- Agent.get(__MODULE__, fn(stats) -> stats end)
+ Agent.get(__MODULE__, fn stats -> stats end)
end
-
end
-
diff --git a/lib/nola/membership.ex b/lib/nola/membership.ex
index 1c7303b..182f44d 100644
--- a/lib/nola/membership.ex
+++ b/lib/nola/membership.ex
@@ -7,11 +7,11 @@ defmodule Nola.Membership do
# Format: {key, last_seen}
defp dets() do
- to_charlist(Nola.data_path <> "/memberships.dets")
+ to_charlist(Nola.data_path() <> "/memberships.dets")
end
def start_link() do
- GenServer.start_link(__MODULE__, [], [name: __MODULE__])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
@@ -25,9 +25,10 @@ defmodule Nola.Membership do
end
def merge_account(old_id, new_id) do
- #iex(37)> :ets.fun2ms(fn({{old_id, _, _}, _}=obj) when old_id == "42" -> obj end)
+ # iex(37)> :ets.fun2ms(fn({{old_id, _, _}, _}=obj) when old_id == "42" -> obj end)
spec = [{{{:"$1", :_, :_}, :_}, [{:==, :"$1", {:const, old_id}}], [:"$_"]}]
- Util.ets_mutate_select_each(:dets, dets(), spec, fn(table, obj = {{_old, net, chan}, ts}) ->
+
+ Util.ets_mutate_select_each(:dets, dets(), spec, fn table, obj = {{_old, net, chan}, ts} ->
:dets.delete_object(table, obj)
:dets.insert(table, {{new_id, net, chan}, ts})
end)
@@ -36,6 +37,7 @@ defmodule Nola.Membership do
def touch(%Nola.Account{id: id}, network, channel) do
:dets.insert(dets(), {{id, network, channel}, NaiveDateTime.utc_now()})
end
+
def touch(account_id, network, channel) do
if account = Nola.Account.get(account_id) do
touch(account, network, channel)
@@ -43,21 +45,28 @@ defmodule Nola.Membership do
end
def notify_channels(account, minutes \\ 30, last_active \\ true) do
- not_before = NaiveDateTime.add(NaiveDateTime.utc_now(), (minutes*-60), :second)
+ not_before = NaiveDateTime.add(NaiveDateTime.utc_now(), minutes * -60, :second)
spec = [{{{:"$1", :_, :_}, :_}, [{:==, :"$1", {:const, account.id}}], [:"$_"]}]
- memberships = :dets.select(dets(), spec)
- |> Enum.sort_by(fn({_, ts}) -> ts end, {:desc, NaiveDateTime})
- active_memberships = Enum.filter(memberships, fn({_, ts}) -> NaiveDateTime.compare(ts, not_before) == :gt end)
+
+ memberships =
+ :dets.select(dets(), spec)
+ |> Enum.sort_by(fn {_, ts} -> ts end, {:desc, NaiveDateTime})
+
+ active_memberships =
+ Enum.filter(memberships, fn {_, ts} -> NaiveDateTime.compare(ts, not_before) == :gt end)
+
cond do
active_memberships == [] && last_active ->
case memberships do
- [{{_, net, chan}, _}|_] -> [{net, chan}]
+ [{{_, net, chan}, _} | _] -> [{net, chan}]
_ -> []
end
+
active_memberships == [] ->
[]
+
true ->
- Enum.map(active_memberships, fn({{_, net, chan}, _}) -> {net,chan} end)
+ Enum.map(active_memberships, fn {{_, net, chan}, _} -> {net, chan} end)
end
end
@@ -78,16 +87,18 @@ defmodule Nola.Membership do
end
def members(network, channel) do
- #iex(19)> :ets.fun2ms(fn({{id, net, chan}, ts}) when net == network and chan == channel and ts > min_seen -> id end)
- limit = 0 # NaiveDateTime.add(NaiveDateTime.utc_now, 30*((24*-60)*60), :second)
+ # iex(19)> :ets.fun2ms(fn({{id, net, chan}, ts}) when net == network and chan == channel and ts > min_seen -> id end)
+ # NaiveDateTime.add(NaiveDateTime.utc_now, 30*((24*-60)*60), :second)
+ limit = 0
+
spec = [
{{{:"$1", :"$2", :"$3"}, :"$4"},
[
- {:andalso,
- {:andalso, {:==, :"$2", {:const, network}}, {:==, :"$3", {:const, channel}}},
+ {:andalso, {:andalso, {:==, :"$2", {:const, network}}, {:==, :"$3", {:const, channel}}},
{:>, :"$4", {:const, limit}}}
], [:"$1"]}
]
+
:dets.select(dets(), spec)
end
@@ -122,8 +133,6 @@ defmodule Nola.Membership do
{account, user, nick}
end
end
- |> Enum.filter(fn(x) -> x end)
+ |> Enum.filter(fn x -> x end)
end
-
-
end
diff --git a/lib/nola/message.ex b/lib/nola/message.ex
index b4e76da..1819e77 100644
--- a/lib/nola/message.ex
+++ b/lib/nola/message.ex
@@ -9,15 +9,14 @@ defmodule Nola.Message do
defstruct [
:id,
:text,
- {:transport, :irc},
- :network,
- :account,
- :sender,
- :channel,
- :trigger,
- :replyfun,
- :at,
- {:meta, %{}}
- ]
-
+ {:transport, :irc},
+ :network,
+ :account,
+ :sender,
+ :channel,
+ :trigger,
+ :replyfun,
+ :at,
+ {:meta, %{}}
+ ]
end
diff --git a/lib/nola/plugins.ex b/lib/nola/plugins.ex
index ac94736..6bfcfec 100644
--- a/lib/nola/plugins.ex
+++ b/lib/nola/plugins.ex
@@ -31,7 +31,7 @@ defmodule Nola.Plugins do
Nola.Plugins.Untappd,
Nola.Plugins.UserMention,
Nola.Plugins.WolframAlpha,
- Nola.Plugins.YouTube,
+ Nola.Plugins.YouTube
]
defmodule Supervisor do
@@ -44,13 +44,23 @@ defmodule Nola.Plugins do
def start_child(module, opts \\ []) do
Logger.info("Starting #{module}")
- spec = %{id: {Nola.Plugins,module}, start: {Nola.Plugins, :start_link, [module, opts]}, name: module, restart: :transient}
+
+ spec = %{
+ id: {Nola.Plugins, module},
+ start: {Nola.Plugins, :start_link, [module, opts]},
+ name: module,
+ restart: :transient
+ }
+
case DynamicSupervisor.start_child(__MODULE__, spec) do
- {:ok, _} = res -> res
+ {:ok, _} = res ->
+ res
+
:ignore ->
Logger.warn("Ignored #{module}")
:ignore
- {:error,_} = res ->
+
+ {:error, _} = res ->
Logger.error("Could not start #{module}: #{inspect(res, pretty: true)}")
res
end
@@ -73,10 +83,14 @@ defmodule Nola.Plugins do
end
def enabled() do
- :dets.foldl(fn
- {name, true, _}, acc -> [name | acc]
- _, acc -> acc
- end, [], dets())
+ :dets.foldl(
+ fn
+ {name, true, _}, acc -> [name | acc]
+ _, acc -> acc
+ end,
+ [],
+ dets()
+ )
end
def start_all() do
@@ -107,10 +121,12 @@ defmodule Nola.Plugins do
@doc "Enables or disables a plugin"
def switch(name, value) when is_boolean(value) do
- last = case get(name) do
- {:ok, last} -> last
- _ -> nil
- end
+ last =
+ case get(name) do
+ {:ok, last} -> last
+ _ -> nil
+ end
+
:dets.insert(dets(), {name, value, last})
end
@@ -124,8 +140,7 @@ defmodule Nola.Plugins do
def start_link(module, options \\ []) do
with {:disabled, {_, true, last}} <- {:disabled, get(module)},
- {:throttled, false} <- {:throttled, false}
- do
+ {:throttled, false} <- {:throttled, false} do
module.start_link()
else
{error, _} ->
@@ -133,5 +148,4 @@ defmodule Nola.Plugins do
:ignore
end
end
-
end
diff --git a/lib/nola/subnet.ex b/lib/nola/subnet.ex
index ac9d8e6..de469a6 100644
--- a/lib/nola/subnet.ex
+++ b/lib/nola/subnet.ex
@@ -17,22 +17,24 @@ defmodule Nola.Subnet do
end
def assign(binary) when is_binary(binary) do
- result = if subnet = find_subnet_for(binary) do
- {:ok, subnet}
- else
- Agent.get_and_update(__MODULE__, fn(dets) ->
- {subnet, _} = available_select(dets)
- :dets.insert(dets, {subnet, binary})
- :dets.sync(dets)
- {{:new, subnet}, dets}
- end)
- end
+ result =
+ if subnet = find_subnet_for(binary) do
+ {:ok, subnet}
+ else
+ Agent.get_and_update(__MODULE__, fn dets ->
+ {subnet, _} = available_select(dets)
+ :dets.insert(dets, {subnet, binary})
+ :dets.sync(dets)
+ {{:new, subnet}, dets}
+ end)
+ end
case result do
{:new, subnet} ->
ip = Pfx.host(subnet, 1)
set_reverse(binary, ip)
subnet
+
{:ok, subnet} ->
subnet
end
@@ -49,16 +51,40 @@ defmodule Nola.Subnet do
ip_fqdn = Pfx.dns_ptr(ip)
ip_local = String.replace(ip_fqdn, ".#{ptr_zone}", "")
rev? = String.ends_with?(value, ".users.goulag.org")
+
if rev? do
{:ok, rev_zone} = PowerDNSex.show_zone("users.goulag.org")
- rev_update? = Enum.any?(rev_zone.rrsets, fn(rr) -> rr.name == "#{ip_fqdn}." end)
- record = %{name: "#{value}.", type: "AAAA", ttl: 8600, records: [%{content: ip, disabled: false}]}
- if(rev_update?, do: PowerDNSex.update_record(rev_zone, record), else: PowerDNSex.create_record(rev_zone, record))
+ rev_update? = Enum.any?(rev_zone.rrsets, fn rr -> rr.name == "#{ip_fqdn}." end)
+
+ record = %{
+ name: "#{value}.",
+ type: "AAAA",
+ ttl: 8600,
+ records: [%{content: ip, disabled: false}]
+ }
+
+ if(rev_update?,
+ do: PowerDNSex.update_record(rev_zone, record),
+ else: PowerDNSex.create_record(rev_zone, record)
+ )
end
+
{:ok, zone} = PowerDNSex.show_zone(ptr_zone)
- update? = Enum.any?(zone.rrsets, fn(rr) -> rr.name == "#{ip_fqdn}." end)
- record = %{name: "#{ip_fqdn}.", type: "PTR", ttl: 3600, records: [%{content: "#{value}.", disabled: false}]}
- pdns = if(update?, do: PowerDNSex.update_record(zone, record), else: PowerDNSex.create_record(zone, record))
+ update? = Enum.any?(zone.rrsets, fn rr -> rr.name == "#{ip_fqdn}." end)
+
+ record = %{
+ name: "#{ip_fqdn}.",
+ type: "PTR",
+ ttl: 3600,
+ records: [%{content: "#{value}.", disabled: false}]
+ }
+
+ pdns =
+ if(update?,
+ do: PowerDNSex.update_record(zone, record),
+ else: PowerDNSex.create_record(zone, record)
+ )
+
:ok
end
@@ -76,9 +102,10 @@ defmodule Nola.Subnet do
defp available_select(dets) do
spec = [{{:"$1", :"$2"}, [is_integer: :"$2"], [{{:"$1", :"$2"}}]}]
{subnets, _} = :dets.select(dets, spec, 20)
- subnet = subnets
- |> Enum.sort_by(fn({_, last}) -> last end)
- |> List.first()
- end
+ subnet =
+ subnets
+ |> Enum.sort_by(fn {_, last} -> last end)
+ |> List.first()
+ end
end
diff --git a/lib/nola/token.ex b/lib/nola/token.ex
index 179bed2..f4fdd86 100644
--- a/lib/nola/token.ex
+++ b/lib/nola/token.ex
@@ -2,15 +2,15 @@ defmodule Nola.Token do
use GenServer
def start_link() do
- GenServer.start_link(__MODULE__, [], [name: __MODULE__])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def lookup(id) do
- with \
- [{_, cred, date}] <- :ets.lookup(__MODULE__.ETS, id),
- IO.inspect("cred: #{inspect cred} valid for #{inspect date} now #{inspect DateTime.utc_now()}"),
- d when d > 0 <- DateTime.diff(date, DateTime.utc_now())
- do
+ with [{_, cred, date}] <- :ets.lookup(__MODULE__.ETS, id),
+ IO.inspect(
+ "cred: #{inspect(cred)} valid for #{inspect(date)} now #{inspect(DateTime.utc_now())}"
+ ),
+ d when d > 0 <- DateTime.diff(date, DateTime.utc_now()) do
{:ok, cred}
else
err -> {:error, err}
@@ -22,17 +22,21 @@ defmodule Nola.Token do
end
def init(_) do
- ets = :ets.new(__MODULE__.ETS, [:ordered_set, :named_table, :protected, {:read_concurrency, true}])
+ ets =
+ :ets.new(__MODULE__.ETS, [:ordered_set, :named_table, :protected, {:read_concurrency, true}])
+
{:ok, ets}
end
def handle_call({:new, cred}, _, ets) do
id = Nola.UserTrack.Id.large_id()
- expire = DateTime.utc_now()
- |> DateTime.add(15*60, :second)
+
+ expire =
+ DateTime.utc_now()
+ |> DateTime.add(15 * 60, :second)
+
obj = {id, cred, expire}
:ets.insert(ets, obj)
{:reply, {:ok, id}, ets}
end
-
end
diff --git a/lib/nola/trigger.ex b/lib/nola/trigger.ex
index 1dec9ac..d3e791b 100644
--- a/lib/nola/trigger.ex
+++ b/lib/nola/trigger.ex
@@ -8,5 +8,4 @@ defmodule Nola.Trigger do
:trigger,
:args
]
-
end
diff --git a/lib/nola/user_track.ex b/lib/nola/user_track.ex
index c1218b0..0b07a91 100644
--- a/lib/nola/user_track.ex
+++ b/lib/nola/user_track.ex
@@ -8,23 +8,23 @@ defmodule Nola.UserTrack do
# Privilege map:
# %{"#channel" => [:operator, :voice]
defmodule Storage do
-
def delete(id) do
- op(fn(ets) -> :ets.delete(ets, id) end)
+ op(fn ets -> :ets.delete(ets, id) end)
end
def insert(tuple) do
- op(fn(ets) -> :ets.insert(ets, tuple) end)
+ op(fn ets -> :ets.insert(ets, tuple) end)
end
def clear_network(network) do
- op(fn(ets) ->
+ op(fn ets ->
spec = [
{{:_, :"$1", :_, :_, :_, :_, :_, :_, :_, :_, :_, :_},
- [
- {:==, :"$1", {:const, network}}
- ], [:"$_"]}
+ [
+ {:==, :"$1", {:const, network}}
+ ], [:"$_"]}
]
+
:ets.match_delete(ets, spec)
end)
end
@@ -34,7 +34,7 @@ defmodule Nola.UserTrack do
end
def start_link do
- GenServer.start_link(__MODULE__, [], [name: __MODULE__])
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
@@ -43,13 +43,15 @@ defmodule Nola.UserTrack do
end
def handle_call({:op, fun}, _from, ets) do
- returned = try do
- {:ok, fun.(ets)}
- rescue
- rescued -> {:error, rescued}
- catch
- rescued -> {:error, rescued}
- end
+ returned =
+ try do
+ {:ok, fun.(ets)}
+ rescue
+ rescued -> {:error, rescued}
+ catch
+ rescued -> {:error, rescued}
+ end
+
{:reply, returned, ets}
end
@@ -58,31 +60,63 @@ defmodule Nola.UserTrack do
end
end
- defmodule Id, do: use EntropyString
+ defmodule Id, do: use(EntropyString)
defmodule User do
- defstruct [:id, :account, :network, :nick, {:nicks, []}, :username, :host, :realname, {:privileges, %{}}, {:last_active, %{}}, {:options, %{}}]
+ defstruct [
+ :id,
+ :account,
+ :network,
+ :nick,
+ {:nicks, []},
+ :username,
+ :host,
+ :realname,
+ {:privileges, %{}},
+ {:last_active, %{}},
+ {:options, %{}}
+ ]
def to_tuple(u = %__MODULE__{}) do
- {u.id || Nola.UserTrack.Id.large_id, u.network, u.account, String.downcase(u.nick), u.nick, u.nicks || [], u.username, u.host, u.realname, u.privileges, u.last_active, u.options}
+ {u.id || Nola.UserTrack.Id.large_id(), u.network, u.account, String.downcase(u.nick),
+ u.nick, u.nicks || [], u.username, u.host, u.realname, u.privileges, u.last_active,
+ u.options}
end
- #tuple size: 11
- def from_tuple({id, network, account, _downcased_nick, nick, nicks, username, host, realname, privs, last_active, opts}) do
- struct = %__MODULE__{id: id, account: account, network: network, nick: nick, nicks: nicks, username: username, host: host, realname: realname, privileges: privs, last_active: last_active, options: opts}
+ # tuple size: 11
+ def from_tuple(
+ {id, network, account, _downcased_nick, nick, nicks, username, host, realname, privs,
+ last_active, opts}
+ ) do
+ struct = %__MODULE__{
+ id: id,
+ account: account,
+ network: network,
+ nick: nick,
+ nicks: nicks,
+ username: username,
+ host: host,
+ realname: realname,
+ privileges: privs,
+ last_active: last_active,
+ options: opts
+ }
end
end
def find_by_account(%Nola.Account{id: id}) do
- #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end)
+ # iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end)
spec = [
{{:_, :_, :"$2", :_, :_, :_, :_, :_, :_, :_, :_, :_},
[
- {:==, :"$2", {:const, id}}
+ {:==, :"$2", {:const, id}}
], [:"$_"]}
]
- results = :ets.select(@ets, spec)
- |> Enum.filter(& &1)
+
+ results =
+ :ets.select(@ets, spec)
+ |> Enum.filter(& &1)
+
for obj <- results, do: User.from_tuple(obj)
end
@@ -91,27 +125,39 @@ defmodule Nola.UserTrack do
end
def find_by_account(network, %Nola.Account{id: id}) do
- #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end)
+ # iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end)
spec = [
{{:_, :"$1", :"$2", :_, :_, :_, :_, :_, :_, :_, :_, :_},
[
- {:andalso, {:==, :"$1", {:const, network}},
- {:==, :"$2", {:const, id}}}
+ {:andalso, {:==, :"$1", {:const, network}}, {:==, :"$2", {:const, id}}}
], [:"$_"]}
]
+
case :ets.select(@ets, spec) do
results = [_r | _] ->
- result = results
- |> Enum.reject(fn({_, net, _, _, _, _, _, _, _, _, actives, opts}) -> network != "matrix" && net == "matrix" end)
- |> Enum.reject(fn({_, net, _, _, _, _, _, _, _, _, actives, opts}) -> network != "telegram" && net == "telegram" end)
- |> Enum.reject(fn({_, _, _, _, _, _, _, _, _, _, actives, opts}) -> network not in ["matrix", "telegram"] && Map.get(opts, :puppet) end)
- |> Enum.sort_by(fn({_, _, _, _, _, _, _, _, _, _, actives, _}) ->
- Map.get(actives, nil)
- end, {:desc, NaiveDateTime})
- |> List.first
+ result =
+ results
+ |> Enum.reject(fn {_, net, _, _, _, _, _, _, _, _, actives, opts} ->
+ network != "matrix" && net == "matrix"
+ end)
+ |> Enum.reject(fn {_, net, _, _, _, _, _, _, _, _, actives, opts} ->
+ network != "telegram" && net == "telegram"
+ end)
+ |> Enum.reject(fn {_, _, _, _, _, _, _, _, _, _, actives, opts} ->
+ network not in ["matrix", "telegram"] && Map.get(opts, :puppet)
+ end)
+ |> Enum.sort_by(
+ fn {_, _, _, _, _, _, _, _, _, _, actives, _} ->
+ Map.get(actives, nil)
+ end,
+ {:desc, NaiveDateTime}
+ )
+ |> List.first()
if result, do: User.from_tuple(result)
- _ -> nil
+
+ _ ->
+ nil
end
end
@@ -119,18 +165,23 @@ defmodule Nola.UserTrack do
Storage.clear_network(network)
end
-
def merge_account(old_id, new_id) do
- #iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end)
+ # iex(15)> :ets.fun2ms(fn(obj = {_, net, acct, _, _, _, _, _, _}) when net == network and acct == account -> obj end)
spec = [
{{:_, :_, :"$1", :_, :_, :_, :_, :_, :_, :_, :_, :_},
[
- {:==, :"$1", {:const, old_id}}
+ {:==, :"$1", {:const, old_id}}
], [:"$_"]}
]
- Enum.each(:ets.select(@ets, spec), fn({id, net, _, downcased_nick, nick, nicks, username, host, realname, privs, active, opts}) ->
- Storage.op(fn(ets) ->
- :ets.insert(@ets, {id, net, new_id, downcased_nick, nick, nicks, username, host, realname, privs, active, opts})
+
+ Enum.each(:ets.select(@ets, spec), fn {id, net, _, downcased_nick, nick, nicks, username,
+ host, realname, privs, active, opts} ->
+ Storage.op(fn ets ->
+ :ets.insert(
+ @ets,
+ {id, net, new_id, downcased_nick, nick, nicks, username, host, realname, privs, active,
+ opts}
+ )
end)
end)
end
@@ -139,14 +190,18 @@ defmodule Nola.UserTrack do
find_by_nick(network, nick)
end
-
def find_by_nick(%ExIRC.SenderInfo{network: network, nick: nick}) do
find_by_nick(network, nick)
end
def find_by_nick(network, nick) do
- case :ets.match(@ets, {:"$1", network, :_, String.downcase(nick), :_, :_, :_, :_, :_, :_, :_, :_}) do
- [[id] | _] -> lookup(id)
+ case :ets.match(
+ @ets,
+ {:"$1", network, :_, String.downcase(nick), :_, :_, :_, :_, :_, :_, :_, :_}
+ ) do
+ [[id] | _] ->
+ lookup(id)
+
_ ->
nil
end
@@ -171,7 +226,7 @@ defmodule Nola.UserTrack do
end
def channel(network, channel) do
- Enum.filter(to_list(), fn({_, network, _, _, _, _, _, _, _, channels, _, _}) ->
+ Enum.filter(to_list(), fn {_, network, _, _, _, _, _, _, _, channels, _, _} ->
Map.get(channels, channel)
end)
end
@@ -179,53 +234,92 @@ defmodule Nola.UserTrack do
# TODO
def connected(network, nick, user, host, account_id, opts \\ %{}) do
if account = Nola.Account.get(account_id) do
- user = if user = find_by_nick(network, nick) do
- user
- else
- user = %User{id: Nola.UserTrack.Id.large_id, account: account_id, network: network, nick: nick, username: user, host: host, privileges: %{}, options: opts}
- Storage.op(fn(ets) ->
- :ets.insert(ets, User.to_tuple(user))
- end)
- user
- end
+ user =
+ if user = find_by_nick(network, nick) do
+ user
+ else
+ user = %User{
+ id: Nola.UserTrack.Id.large_id(),
+ account: account_id,
+ network: network,
+ nick: nick,
+ username: user,
+ host: host,
+ privileges: %{},
+ options: opts
+ }
+
+ Storage.op(fn ets ->
+ :ets.insert(ets, User.to_tuple(user))
+ end)
+
+ user
+ end
+
+ Nola.Irc.Connection.publish_event(network, %{
+ type: :connect,
+ user_id: user.id,
+ account_id: user.account
+ })
- Nola.Irc.Connection.publish_event(network, %{type: :connect, user_id: user.id, account_id: user.account})
:ok
else
:error
end
end
- def joined(c, s), do: joined(c,s,[])
+ def joined(c, s), do: joined(c, s, [])
- def joined(channel, sender=%{nick: nick, user: uname, host: host}, privileges, touch \\ true) do
- privileges = if Nola.Irc.admin?(sender) do
- privileges ++ [:admin]
- else privileges end
- user = if user = find_by_nick(sender.network, nick) do
- %User{user | username: uname, host: host, privileges: Map.put(user.privileges || %{}, channel, privileges)}
- else
- user = %User{id: Nola.UserTrack.Id.large_id, network: sender.network, nick: nick, username: uname, host: host, privileges: %{channel => privileges}}
+ def joined(channel, sender = %{nick: nick, user: uname, host: host}, privileges, touch \\ true) do
+ privileges =
+ if Nola.Irc.admin?(sender) do
+ privileges ++ [:admin]
+ else
+ privileges
+ end
+
+ user =
+ if user = find_by_nick(sender.network, nick) do
+ %User{
+ user
+ | username: uname,
+ host: host,
+ privileges: Map.put(user.privileges || %{}, channel, privileges)
+ }
+ else
+ user = %User{
+ id: Nola.UserTrack.Id.large_id(),
+ network: sender.network,
+ nick: nick,
+ username: uname,
+ host: host,
+ privileges: %{channel => privileges}
+ }
+
+ account = Nola.Account.lookup(user).id
+ user = %User{user | account: account}
+ end
- account = Nola.Account.lookup(user).id
- user = %User{user | account: account}
- end
user = touch_struct(user, channel)
if touch && user.account do
Nola.Membership.touch(user.account, sender.network, channel)
end
- Storage.op(fn(ets) ->
+ Storage.op(fn ets ->
:ets.insert(ets, User.to_tuple(user))
end)
- Nola.Irc.Connection.publish_event({sender.network, channel}, %{type: :join, user_id: user.id, account_id: user.account})
+ Nola.Irc.Connection.publish_event({sender.network, channel}, %{
+ type: :join,
+ user_id: user.id,
+ account_id: user.account
+ })
user
end
- #def joined(network, channel, nick, privileges) do
+ # def joined(network, channel, nick, privileges) do
# user = if user = find_by_nick(network, nick) do
# %User{user | privileges: Map.put(user.privileges, channel, privileges)}
# else
@@ -235,18 +329,24 @@ defmodule Nola.UserTrack do
# Storage.op(fn(ets) ->
# :ets.insert(ets, User.to_tuple(user))
# end)
- #end
+ # end
+
+ def messaged(
+ %Nola.Message{network: network, account: account, channel: chan, sender: %{nick: nick}} =
+ m
+ ) do
+ {user, account} =
+ if user = find_by_nick(network, nick) do
+ {touch_struct(user, chan), account || Nola.Account.lookup(user)}
+ else
+ user = %User{network: network, nick: nick, privileges: %{}}
+ account = Nola.Account.lookup(user)
+ {%User{user | account: account.id}, account}
+ end
- def messaged(%Nola.Message{network: network, account: account, channel: chan, sender: %{nick: nick}} = m) do
- {user, account} = if user = find_by_nick(network, nick) do
- {touch_struct(user, chan), account || Nola.Account.lookup(user)}
- else
- user = %User{network: network, nick: nick, privileges: %{}}
- account = Nola.Account.lookup(user)
- {%User{user | account: account.id}, account}
- end
Storage.insert(User.to_tuple(user))
if chan, do: Nola.Membership.touch(account, network, chan)
+
if !m.account do
{:ok, %Nola.Message{m | account: account}}
else
@@ -257,12 +357,19 @@ defmodule Nola.UserTrack do
def renamed(network, old_nick, new_nick) do
if user = find_by_nick(network, old_nick) do
old_account = Nola.Account.lookup(user)
- user = %User{user | nick: new_nick, nicks: [old_nick|user.nicks]}
+ user = %User{user | nick: new_nick, nicks: [old_nick | user.nicks]}
account = Nola.Account.lookup(user, false) || old_account
- user = %User{user | nick: new_nick, account: account.id, nicks: [old_nick|user.nicks]}
+ user = %User{user | nick: new_nick, account: account.id, nicks: [old_nick | user.nicks]}
Storage.insert(User.to_tuple(user))
channels = for {channel, _} <- user.privileges, do: channel
- Nola.Irc.Connection.publish_event(network, %{type: :nick, user_id: user.id, account_id: account.id, nick: new_nick, old_nick: old_nick})
+
+ Nola.Irc.Connection.publish_event(network, %{
+ type: :nick,
+ user_id: user.id,
+ account_id: account.id,
+ nick: new_nick,
+ old_nick: old_nick
+ })
end
end
@@ -270,12 +377,19 @@ defmodule Nola.UserTrack do
if user = find_by_nick(network, nick) do
privs = Map.get(user.privileges, channel)
- privs = Enum.reduce(add, privs, fn(priv, acc) -> [priv|acc] end)
- privs = Enum.reduce(remove, privs, fn(priv, acc) -> List.delete(acc, priv) end)
+ privs = Enum.reduce(add, privs, fn priv, acc -> [priv | acc] end)
+ privs = Enum.reduce(remove, privs, fn priv, acc -> List.delete(acc, priv) end)
user = %User{user | privileges: Map.put(user.privileges, channel, privs)}
Storage.insert(User.to_tuple(user))
- Nola.Irc.Connection.publish_event({network, channel}, %{type: :privileges, user_id: user.id, account_id: user.account, added: add, removed: remove})
+
+ Nola.Irc.Connection.publish_event({network, channel}, %{
+ type: :privileges,
+ user_id: user.id,
+ account_id: user.account,
+ added: add,
+ removed: remove
+ })
end
end
@@ -292,12 +406,25 @@ defmodule Nola.UserTrack do
privs = Map.delete(user.privileges, channel)
lasts = Map.delete(user.last_active, channel)
+
if Enum.count(privs) > 0 do
user = %User{user | privileges: privs}
Storage.insert(User.to_tuple(user))
- Nola.Irc.Connection.publish_event({network, channel}, %{type: :part, user_id: user.id, account_id: user.account, reason: nil})
+
+ Nola.Irc.Connection.publish_event({network, channel}, %{
+ type: :part,
+ user_id: user.id,
+ account_id: user.account,
+ reason: nil
+ })
else
- Nola.Irc.Connection.publish_event(network, %{type: :quit, user_id: user.id, account_id: user.account, reason: "Left all known channels"})
+ Nola.Irc.Connection.publish_event(network, %{
+ type: :quit,
+ user_id: user.id,
+ account_id: user.account,
+ reason: "Left all known channels"
+ })
+
Storage.delete(user.id)
end
end
@@ -309,17 +436,27 @@ defmodule Nola.UserTrack do
for {channel, _} <- user.privileges do
Nola.Membership.touch(user.account, sender.network, channel)
end
- Nola.Irc.Connection.publish_event(sender.network, %{type: :quit, user_id: user.id, account_id: user.account, reason: reason})
+
+ Nola.Irc.Connection.publish_event(sender.network, %{
+ type: :quit,
+ user_id: user.id,
+ account_id: user.account,
+ reason: reason
+ })
end
+
Storage.delete(user.id)
end
end
defp touch_struct(user = %User{last_active: last_active}, channel) do
now = NaiveDateTime.utc_now()
- last_active = last_active
- |> Map.put(channel, now)
- |> Map.put(nil, now)
+
+ last_active =
+ last_active
+ |> Map.put(channel, now)
+ |> Map.put(nil, now)
+
%User{user | last_active: last_active}
end
diff --git a/lib/open_ai.ex b/lib/open_ai.ex
index da54e3a..2b8783f 100644
--- a/lib/open_ai.ex
+++ b/lib/open_ai.ex
@@ -1,33 +1,40 @@
defmodule OpenAi do
require Logger
-
def post(path, data, options \\ []) do
config = Application.get_env(:nola, :openai, [])
base_url = Keyword.get(config, :base_url, "https://api.openai.com")
url = "#{base_url}#{path}"
- headers = [{"user-agent", "internal private experiment bot, href@random.sh"},
- {"content-type", "application/json"},
- {"authorization", "Bearer " <> Keyword.get(config, :key, "unset-api-key")}]
+
+ headers = [
+ {"user-agent", "internal private experiment bot, href@random.sh"},
+ {"content-type", "application/json"},
+ {"authorization", "Bearer " <> Keyword.get(config, :key, "unset-api-key")}
+ ]
+
options = options ++ [timeout: :timer.seconds(30), recv_timeout: :timer.seconds(30)]
- Logger.debug("openai: post: #{url} #{inspect data}")
+ Logger.debug("openai: post: #{url} #{inspect(data)}")
+
with {:ok, json} <- Poison.encode(data),
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.post(url, json, headers, options),
+ {:ok, %HTTPoison.Response{status_code: 200, body: body}} <-
+ HTTPoison.post(url, json, headers, options),
{:ok, data} <- Poison.decode(body) do
{:ok, data}
else
{:ok, %HTTPoison.Response{status_code: code, body: body}} ->
- Logger.error("OpenAI: HTTP #{code} #{inspect body}")
+ Logger.error("OpenAI: HTTP #{code} #{inspect(body)}")
status = Plug.Conn.Status.reason_atom(code)
+
case Poison.decode(body) do
{:ok, %{"error" => %{"message" => message, "code" => code}}} ->
{:error, {status, message}}
+
kek ->
{:error, status}
end
+
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, reason}
end
end
-
end
diff --git a/lib/plugins/account.ex b/lib/plugins/account.ex
index 0377e1c..2977f4b 100644
--- a/lib/plugins/account.ex
+++ b/lib/plugins/account.ex
@@ -1,187 +1,219 @@
defmodule Nola.Plugins.Account do
- @moduledoc """
- # Account
-
- * **account** Get current account id and token
- * **auth `<account-id>` `<token>`** Authenticate and link the current nickname to an account
- * **auth** list authentications methods
- * **whoami** list currently authenticated users
- * **web** get a one-time login link to web
- * **enable-telegram** Link a Telegram account
- * **enable-sms** Link a SMS number
- * **enable-untappd** Link a Untappd account
- * **set-name** set account name
- * **setusermeta puppet-nick `<nick>`** Set puppet IRC nickname
- """
-
- def irc_doc, do: @moduledoc
- def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
- def init(_) do
- {:ok, _} = Registry.register(Nola.PubSub, "messages:private", [])
- {:ok, nil}
- end
+ @moduledoc """
+ # Account
+
+ * **account** Get current account id and token
+ * **auth `<account-id>` `<token>`** Authenticate and link the current nickname to an account
+ * **auth** list authentications methods
+ * **whoami** list currently authenticated users
+ * **web** get a one-time login link to web
+ * **enable-telegram** Link a Telegram account
+ * **enable-sms** Link a SMS number
+ * **enable-untappd** Link a Untappd account
+ * **set-name** set account name
+ * **setusermeta puppet-nick `<nick>`** Set puppet IRC nickname
+ """
+
+ def irc_doc, do: @moduledoc
+ def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
+
+ def init(_) do
+ {:ok, _} = Registry.register(Nola.PubSub, "messages:private", [])
+ {:ok, nil}
+ end
- def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "help"}}, state) do
- text = [
- "account: show current account and auth token",
- "auth: show authentications methods",
- "whoami: list authenticated users",
- "set-name <name>: set account name",
- "web: login to web",
- "enable-sms | disable-sms: enable/change or disable sms",
- "enable-telegram: link/change telegram",
- "enable-untappd: link untappd account",
- "getmeta: show meta datas",
- "setusermeta: set user meta",
- ]
- m.replyfun.(text)
- {:noreply, state}
- end
+ def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "help"}}, state) do
+ text = [
+ "account: show current account and auth token",
+ "auth: show authentications methods",
+ "whoami: list authenticated users",
+ "set-name <name>: set account name",
+ "web: login to web",
+ "enable-sms | disable-sms: enable/change or disable sms",
+ "enable-telegram: link/change telegram",
+ "enable-untappd: link untappd account",
+ "getmeta: show meta datas",
+ "setusermeta: set user meta"
+ ]
+
+ m.replyfun.(text)
+ {:noreply, state}
+ end
- def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "auth"}}, state) do
- spec = [{{:"$1", :"$2"}, [{:==, :"$2", {:const, account.id}}], [:"$1"]}]
- predicates = :dets.select(Nola.Account.file("predicates"), spec)
- text = for {net, {key, value}} <- predicates, do: "#{net}: #{to_string(key)}: #{value}"
- m.replyfun.(text)
- {:noreply, state}
- end
+ def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "auth"}}, state) do
+ spec = [{{:"$1", :"$2"}, [{:==, :"$2", {:const, account.id}}], [:"$1"]}]
+ predicates = :dets.select(Nola.Account.file("predicates"), spec)
+ text = for {net, {key, value}} <- predicates, do: "#{net}: #{to_string(key)}: #{value}"
+ m.replyfun.(text)
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "whoami"}}, state) do
+ users =
+ for user <- Nola.UserTrack.find_by_account(m.account) do
+ chans =
+ Enum.map(user.privileges, fn {chan, _} -> chan end)
+ |> Enum.join(" ")
- def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "whoami"}}, state) do
- users = for user <- Nola.UserTrack.find_by_account(m.account) do
- chans = Enum.map(user.privileges, fn({chan, _}) -> chan end)
- |> Enum.join(" ")
"#{user.network} - #{user.nick}!#{user.username}@#{user.host} - #{chans}"
end
- m.replyfun.(users)
- {:noreply, state}
- end
- def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "account"}}, state) do
- account = Nola.Account.lookup(m.sender)
- text = ["Account Id: #{account.id}",
- "Authenticate to this account from another network: \"auth #{account.id} #{account.token}\" to the other bot!"]
- m.replyfun.(text)
- {:noreply, state}
- end
+ m.replyfun.(users)
+ {:noreply, state}
+ end
- def handle_info({:irc, :text, m = %Nola.Message{sender: sender, text: "auth"<>_}}, state) do
- #account = Nola.Account.lookup(m.sender)
- case String.split(m.text, " ") do
- ["auth", id, token] ->
- join_account(m, id, token)
- _ ->
- m.replyfun.("Invalid parameters")
- end
- {:noreply, state}
- end
+ def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "account"}}, state) do
+ account = Nola.Account.lookup(m.sender)
- def handle_info({:irc, :text, m = %Nola.Message{account: account, text: "set-name "<>name}}, state) do
- Nola.Account.update_account_name(account, name)
- m.replyfun.("Name changed: #{name}")
- {:noreply, state}
- end
+ text = [
+ "Account Id: #{account.id}",
+ "Authenticate to this account from another network: \"auth #{account.id} #{account.token}\" to the other bot!"
+ ]
- def handle_info({:irc, :text, m = %Nola.Message{text: "disable-sms"}}, state) do
- if Nola.Account.get_meta(m.account, "sms-number") do
- Nola.Account.delete_meta(m.account, "sms-number")
- m.replyfun.("SMS disabled.")
- else
- m.replyfun.("SMS already disabled.")
- end
- {:noreply, state}
- end
+ m.replyfun.(text)
+ {:noreply, state}
+ end
- def handle_info({:irc, :text, m = %Nola.Message{text: "web"}}, state) do
- login_url = Nola.AuthToken.new_url(m.account.id, nil)
- m.replyfun.("↪:" <> login_url)
- {:noreply, state}
- end
+ def handle_info({:irc, :text, m = %Nola.Message{sender: sender, text: "auth" <> _}}, state) do
+ # account = Nola.Account.lookup(m.sender)
+ case String.split(m.text, " ") do
+ ["auth", id, token] ->
+ join_account(m, id, token)
- def handle_info({:irc, :text, m = %Nola.Message{text: "enable-sms"}}, state) do
- code = String.downcase(EntropyString.small_id())
- Nola.Account.put_meta(m.account, "sms-validation-code", code)
- Nola.Account.put_meta(m.account, "sms-validation-target", m.network)
- number = Nola.Plugins.Sms.my_number()
- text = "To enable or change your number for SMS messaging, please send:"
- <> " \"enable #{code}\" to #{number}"
- m.replyfun.(text)
- {:noreply, state}
+ _ ->
+ m.replyfun.("Invalid parameters")
end
- def handle_info({:irc, :text, m = %Nola.Message{text: "enable-telegram"}}, state) do
- code = String.downcase(EntropyString.small_id())
- Nola.Account.delete_meta(m.account, "telegram-id")
- Nola.Account.put_meta(m.account, "telegram-validation-code", code)
- Nola.Account.put_meta(m.account, "telegram-validation-target", m.network)
- text = "To enable or change your number for telegram messaging, please open #{Nola.Telegram.my_path()} and send:"
- <> " \"/enable #{code}\""
- m.replyfun.(text)
- {:noreply, state}
- end
+ {:noreply, state}
+ end
+
+ def handle_info(
+ {:irc, :text, m = %Nola.Message{account: account, text: "set-name " <> name}},
+ state
+ ) do
+ Nola.Account.update_account_name(account, name)
+ m.replyfun.("Name changed: #{name}")
+ {:noreply, state}
+ end
- def handle_info({:irc, :text, m = %Nola.Message{text: "enable-untappd"}}, state) do
- auth_url = Untappd.auth_url()
- login_url = Nola.AuthToken.new_url(m.account.id, {:external_redirect, auth_url})
- m.replyfun.(["To link your Untappd account, open this URL:", login_url])
- {:noreply, state}
+ def handle_info({:irc, :text, m = %Nola.Message{text: "disable-sms"}}, state) do
+ if Nola.Account.get_meta(m.account, "sms-number") do
+ Nola.Account.delete_meta(m.account, "sms-number")
+ m.replyfun.("SMS disabled.")
+ else
+ m.replyfun.("SMS already disabled.")
end
- def handle_info({:irc, :text, m = %Nola.Message{text: "getmeta"<>_}}, state) do
- result = case String.split(m.text, " ") do
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :text, m = %Nola.Message{text: "web"}}, state) do
+ login_url = Nola.AuthToken.new_url(m.account.id, nil)
+ m.replyfun.("↪:" <> login_url)
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :text, m = %Nola.Message{text: "enable-sms"}}, state) do
+ code = String.downcase(EntropyString.small_id())
+ Nola.Account.put_meta(m.account, "sms-validation-code", code)
+ Nola.Account.put_meta(m.account, "sms-validation-target", m.network)
+ number = Nola.Plugins.Sms.my_number()
+
+ text =
+ "To enable or change your number for SMS messaging, please send:" <>
+ " \"enable #{code}\" to #{number}"
+
+ m.replyfun.(text)
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :text, m = %Nola.Message{text: "enable-telegram"}}, state) do
+ code = String.downcase(EntropyString.small_id())
+ Nola.Account.delete_meta(m.account, "telegram-id")
+ Nola.Account.put_meta(m.account, "telegram-validation-code", code)
+ Nola.Account.put_meta(m.account, "telegram-validation-target", m.network)
+
+ text =
+ "To enable or change your number for telegram messaging, please open #{Nola.Telegram.my_path()} and send:" <>
+ " \"/enable #{code}\""
+
+ m.replyfun.(text)
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :text, m = %Nola.Message{text: "enable-untappd"}}, state) do
+ auth_url = Untappd.auth_url()
+ login_url = Nola.AuthToken.new_url(m.account.id, {:external_redirect, auth_url})
+ m.replyfun.(["To link your Untappd account, open this URL:", login_url])
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :text, m = %Nola.Message{text: "getmeta" <> _}}, state) do
+ result =
+ case String.split(m.text, " ") do
["getmeta"] ->
for {k, v} <- Nola.Account.get_all_meta(m.account) do
case k do
- "u:"<>key -> "(user) #{key}: #{v}"
+ "u:" <> key -> "(user) #{key}: #{v}"
key -> "#{key}: #{v}"
end
end
+
["getmeta", key] ->
value = Nola.Account.get_meta(m.account, key)
- text = if value do
- "#{key}: #{value}"
- else
- "#{key} is not defined"
- end
+
+ text =
+ if value do
+ "#{key}: #{value}"
+ else
+ "#{key} is not defined"
+ end
+
_ ->
"usage: getmeta [key]"
end
- m.replyfun.(result)
- {:noreply, state}
- end
- def handle_info({:irc, :text, m = %Nola.Message{text: "setusermeta"<>_}}, state) do
- result = case String.split(m.text, " ") do
+ m.replyfun.(result)
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :text, m = %Nola.Message{text: "setusermeta" <> _}}, state) do
+ result =
+ case String.split(m.text, " ") do
["setusermeta", key, value] ->
Nola.Account.put_user_meta(m.account, key, value)
"ok"
+
_ ->
"usage: setusermeta <key> <value>"
end
- m.replyfun.(result)
- {:noreply, state}
- end
- def handle_info(_, state) do
- {:noreply, state}
- end
+ m.replyfun.(result)
+ {:noreply, state}
+ end
- defp join_account(m, id, token) do
- old_account = Nola.Account.lookup(m.sender)
- new_account = Nola.Account.get(id)
- if new_account && token == new_account.token do
- case Nola.Account.merge_account(old_account.id, new_account.id) do
- :ok ->
- if old_account.id == new_account.id do
- m.replyfun.("Already authenticated, but hello")
- else
- m.replyfun.("Accounts merged!")
- end
- _ -> m.replyfun.("Something failed :(")
- end
- else
- m.replyfun.("Invalid token")
+ def handle_info(_, state) do
+ {:noreply, state}
+ end
+
+ defp join_account(m, id, token) do
+ old_account = Nola.Account.lookup(m.sender)
+ new_account = Nola.Account.get(id)
+
+ if new_account && token == new_account.token do
+ case Nola.Account.merge_account(old_account.id, new_account.id) do
+ :ok ->
+ if old_account.id == new_account.id do
+ m.replyfun.("Already authenticated, but hello")
+ else
+ m.replyfun.("Accounts merged!")
+ end
+
+ _ ->
+ m.replyfun.("Something failed :(")
end
+ else
+ m.replyfun.("Invalid token")
end
-
end
-
+end
diff --git a/lib/plugins/alcoolog.ex b/lib/plugins/alcoolog.ex
index 69bd60c..de69b13 100644
--- a/lib/plugins/alcoolog.ex
+++ b/lib/plugins/alcoolog.ex
@@ -49,27 +49,40 @@ defmodule Nola.Plugins.Alcoolog do
@default_user_meta %{weight: 77.4, sex: true, loss_factor: 15}
def data_state() do
- dets_filename = (Nola.data_path() <> "/" <> "alcoolisme.dets") |> String.to_charlist
- dets_meta_filename = (Nola.data_path() <> "/" <> "alcoolisme_meta.dets") |> String.to_charlist
+ dets_filename = (Nola.data_path() <> "/" <> "alcoolisme.dets") |> String.to_charlist()
+
+ dets_meta_filename =
+ (Nola.data_path() <> "/" <> "alcoolisme_meta.dets") |> String.to_charlist()
+
%{dets: dets_filename, meta: dets_meta_filename, ets: __MODULE__.ETS}
end
def init(_) do
- triggers = for(t <- @pubsub_triggers, do: "trigger:"<>t)
+ triggers = for(t <- @pubsub_triggers, do: "trigger:" <> t)
+
for sub <- @pubsub ++ triggers do
{:ok, _} = Registry.register(Nola.PubSub, sub, plugin: __MODULE__)
end
- dets_filename = (Nola.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}])
- dets_meta_filename = (Nola.data_path() <> "/" <> "alcoolisme_meta.dets") |> String.to_charlist
- {:ok, meta} = :dets.open_file(dets_meta_filename, [{:type,:set}])
- traverse_fun = fn(obj, dets) ->
+
+ dets_filename = (Nola.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}])
+
+ dets_meta_filename =
+ (Nola.data_path() <> "/" <> "alcoolisme_meta.dets") |> String.to_charlist()
+
+ {:ok, meta} = :dets.open_file(dets_meta_filename, [{:type, :set}])
+
+ traverse_fun = fn obj, dets ->
case obj do
object = {nick, naive = %NaiveDateTime{}, volumes, active, cl, deg, name, comment} ->
- date = naive
- |> DateTime.from_naive!("Etc/UTC")
- |> DateTime.to_unix()
+ date =
+ naive
+ |> DateTime.from_naive!("Etc/UTC")
+ |> DateTime.to_unix()
+
new = {nick, date, volumes, active, cl, deg, name, comment, Map.new()}
:dets.delete_object(dets, object)
:dets.insert(dets, new)
@@ -77,9 +90,11 @@ defmodule Nola.Plugins.Alcoolog do
dets
object = {nick, naive = %NaiveDateTime{}, volumes, active, cl, deg, name, comment, meta} ->
- date = naive
- |> DateTime.from_naive!("Etc/UTC")
- |> DateTime.to_unix()
+ date =
+ naive
+ |> DateTime.from_naive!("Etc/UTC")
+ |> DateTime.to_unix()
+
new = {nick, date, volumes, active, cl, deg, name, comment, Map.new()}
:dets.delete_object(dets, object)
:dets.insert(dets, new)
@@ -94,6 +109,7 @@ defmodule Nola.Plugins.Alcoolog do
dets
end
end
+
:dets.foldl(traverse_fun, dets, dets)
:dets.sync(dets)
state = %{dets: dets, meta: meta, ets: ets}
@@ -101,59 +117,82 @@ defmodule Nola.Plugins.Alcoolog do
end
@eau ["santo", "santeau"]
- def handle_info({:irc, :trigger, santeau, m = %Nola.Message{trigger: %Nola.Trigger{args: _, type: :bang}}}, state) when santeau in @eau do
+ def handle_info(
+ {:irc, :trigger, santeau,
+ m = %Nola.Message{trigger: %Nola.Trigger{args: _, type: :bang}}},
+ state
+ )
+ when santeau in @eau do
Nola.Plugins.Txt.reply_random(m, "alcoolog.santo")
{:noreply, state}
end
- def handle_info({:irc, :trigger, "soif", m = %Nola.Message{trigger: %Nola.Trigger{args: _, type: :bang}}}, state) do
- now = DateTime.utc_now()
- |> Timex.Timezone.convert("Europe/Paris")
+ def handle_info(
+ {:irc, :trigger, "soif", m = %Nola.Message{trigger: %Nola.Trigger{args: _, type: :bang}}},
+ state
+ ) do
+ now =
+ DateTime.utc_now()
+ |> Timex.Timezone.convert("Europe/Paris")
+
apero = format_duration_from_now(%DateTime{now | hour: 18, minute: 0, second: 0}, false)
day_of_week = Date.day_of_week(now)
- {txt, apero?} = cond do
- now.hour >= 0 && now.hour < 6 ->
- {["apéro tardif ? Je dis OUI ! SANTAI !"], true}
- now.hour >= 6 && now.hour < 12 ->
- if day_of_week >= 6 do
- {["de l'alcool pour le petit dej ? le week-end, pas de problème !"], true}
- else
- {["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})",
- "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}",
- "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
- |> Enum.shuffle()
- |> Enum.random()
+ {txt, apero?} =
+ cond do
+ now.hour >= 0 && now.hour < 6 ->
+ {["apéro tardif ? Je dis OUI ! SANTAI !"], true}
+
+ now.hour >= 6 && now.hour < 12 ->
+ if day_of_week >= 6 do
+ {["de l'alcool pour le petit dej ? le week-end, pas de problème !"], true}
+ else
+ {["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})",
+ "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}",
+ "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
+ |> Enum.shuffle()
+ |> Enum.random()
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
@@ -161,99 +200,151 @@ defmodule Nola.Plugins.Alcoolog do
{:noreply, state}
end
- def handle_info({:irc, :trigger, "sobrepour", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "sobrepour",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}},
+ state
+ ) do
args = Enum.join(args, " ")
{:ok, now} = DateTime.now("Europe/Paris", Tzdata.TimeZoneDatabase)
- time = case args do
- "demain " <> time ->
- {h, m} = case String.split(time, [":", "h"]) do
- [hour, ""] ->
- IO.puts ("h #{inspect hour}")
- {h, _} = Integer.parse(hour)
- {h, 0}
- [hour, min] when min != "" ->
- {h, _} = Integer.parse(hour)
- {m, _} = Integer.parse(min)
- {h, m}
- [hour] ->
- IO.puts ("h #{inspect hour}")
- {h, _} = Integer.parse(hour)
- {h, 0}
- _ -> {0, 0}
- end
- secs = ((60*60)*24)
- day = DateTime.add(now, secs, :second, Tzdata.TimeZoneDatabase)
- %DateTime{day | hour: h, minute: m, second: 0}
- "après demain " <> time ->
- secs = 2*((60*60)*24)
- DateTime.add(now, secs, :second, Tzdata.TimeZoneDatabase)
- datetime ->
- case Timex.Parse.DateTime.Parser.parse(datetime, "{}") do
- {:ok, dt} -> dt
- _ -> nil
- end
- end
+
+ time =
+ case args do
+ "demain " <> time ->
+ {h, m} =
+ case String.split(time, [":", "h"]) do
+ [hour, ""] ->
+ IO.puts("h #{inspect(hour)}")
+ {h, _} = Integer.parse(hour)
+ {h, 0}
+
+ [hour, min] when min != "" ->
+ {h, _} = Integer.parse(hour)
+ {m, _} = Integer.parse(min)
+ {h, m}
+
+ [hour] ->
+ IO.puts("h #{inspect(hour)}")
+ {h, _} = Integer.parse(hour)
+ {h, 0}
+
+ _ ->
+ {0, 0}
+ end
+
+ secs = 60 * 60 * 24
+ day = DateTime.add(now, secs, :second, Tzdata.TimeZoneDatabase)
+ %DateTime{day | hour: h, minute: m, second: 0}
+
+ "après demain " <> time ->
+ secs = 2 * (60 * 60 * 24)
+ DateTime.add(now, secs, :second, Tzdata.TimeZoneDatabase)
+
+ datetime ->
+ case Timex.Parse.DateTime.Parser.parse(datetime, "{}") do
+ {:ok, dt} -> dt
+ _ -> nil
+ end
+ end
if time do
meta = get_user_meta(state, m.account.id)
stats = get_full_statistics(state, m.account.id)
- duration = round(DateTime.diff(time, now)/60.0)
+ duration = round(DateTime.diff(time, now) / 60.0)
- IO.puts "diff #{inspect duration} sober in #{inspect stats.sober_in}"
+ IO.puts("diff #{inspect(duration)} sober in #{inspect(stats.sober_in)}")
if duration < stats.sober_in do
int = stats.sober_in - duration
m.replyfun.("désolé, aucune chance! tu seras sobre #{format_minute_duration(int)} après!")
else
remaining = duration - stats.sober_in
+
if remaining < 30 do
m.replyfun.("moins de 30 minutes de sobriété, c'est impossible de boire plus")
else
- loss_per_minute = ((meta.loss_factor/100)/60)
- remaining_gl = (remaining-30)*loss_per_minute
- m.replyfun.("marge de boisson: #{inspect remaining} minutes, #{remaining_gl} g/l")
+ loss_per_minute = meta.loss_factor / 100 / 60
+ remaining_gl = (remaining - 30) * loss_per_minute
+ m.replyfun.("marge de boisson: #{inspect(remaining)} minutes, #{remaining_gl} g/l")
end
end
-
end
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "alcoolog", m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :plus}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "alcoolog",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :plus}}},
+ state
+ ) do
{:ok, token} = Nola.Token.new({:alcoolog, :index, m.sender.network, m.channel})
- url = NolaWeb.Router.Helpers.alcoolog_url(NolaWeb.Endpoint, :index, m.network, NolaWeb.format_chan(m.channel), token)
+
+ url =
+ NolaWeb.Router.Helpers.alcoolog_url(
+ NolaWeb.Endpoint,
+ :index,
+ m.network,
+ NolaWeb.format_chan(m.channel),
+ token
+ )
+
m.replyfun.("-> #{url}")
{:noreply, state}
end
- def handle_info({:irc, :trigger, "alcoolog", m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :bang}}}, state) do
- url = NolaWeb.Router.Helpers.alcoolog_url(NolaWeb.Endpoint, :index, m.network, NolaWeb.format_chan(m.channel))
+ def handle_info(
+ {:irc, :trigger, "alcoolog",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :bang}}},
+ state
+ ) do
+ url =
+ NolaWeb.Router.Helpers.alcoolog_url(
+ NolaWeb.Endpoint,
+ :index,
+ m.network,
+ NolaWeb.format_chan(m.channel)
+ )
+
m.replyfun.("-> #{url}")
{:noreply, state}
end
- def handle_info({:irc, :trigger, "alcool", m = %Nola.Message{trigger: %Nola.Trigger{args: args = [cl, deg], type: :bang}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "alcool",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args = [cl, deg], type: :bang}}},
+ state
+ ) do
{cl, _} = Util.float_paparse(cl)
{deg, _} = Util.float_paparse(deg)
points = Alcool.units(cl, deg)
meta = get_user_meta(state, m.account.id)
k = if meta.sex, do: 0.7, else: 0.6
weight = meta.weight
- gl = (10*points)/(k*weight)
- duration = round(gl/((meta.loss_factor/100)/60))+30
- sober_in_s = if duration > 0 do
+ gl = 10 * points / (k * weight)
+ duration = round(gl / (meta.loss_factor / 100 / 60)) + 30
+
+ sober_in_s =
+ if duration > 0 do
duration = Timex.Duration.from_minutes(duration)
- Timex.Format.Duration.Formatter.lformat(duration, "fr", :humanized)
- else
- ""
- end
+ Timex.Format.Duration.Formatter.lformat(duration, "fr", :humanized)
+ else
+ ""
+ end
+
+ m.replyfun.(
+ "Il y a #{Float.round(points + 0.0, 4)} unités d'alcool dans #{cl}cl à #{deg}° (#{Float.round(gl + 0.0, 4)} g/l, #{sober_in_s})"
+ )
- m.replyfun.("Il y a #{Float.round(points+0.0, 4)} unités d'alcool dans #{cl}cl à #{deg}° (#{Float.round(gl + 0.0, 4)} g/l, #{sober_in_s})")
{:noreply, state}
end
- def handle_info({:irc, :trigger, "santai", m = %Nola.Message{trigger: %Nola.Trigger{args: [cl, deg | comment], type: :bang}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "santai",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [cl, deg | comment], type: :bang}}},
+ state
+ ) do
santai(m, state, cl, deg, comment)
{:noreply, state}
end
@@ -263,80 +354,138 @@ defmodule Nola.Plugins.Alcoolog do
"{{message.sender.nick}}: et voilà la petite sœur !"
]
- def handle_info({:irc, :trigger, "bis", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "bis",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}},
+ state
+ ) do
handle_info({:irc, :trigger, "moar", m}, state)
end
- def handle_info({:irc, :trigger, "again", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}}, state) do
+
+ def handle_info(
+ {:irc, :trigger, "again",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}},
+ state
+ ) do
handle_info({:irc, :trigger, "moar", m}, state)
end
- def handle_info({:irc, :trigger, "moar", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "moar",
+ m = %Nola.Message{trigger: %Nola.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
+ 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")
+ 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
+
+ _ ->
+ 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
+ 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, "("<>_} ->
- try do
- {:ok, result} = Abacus.eval(cl)
- {result, nil}
- rescue
- _ -> {nil, "cl: invalid calc expression"}
- end
- {:error, _} -> {nil, "cl: invalid value"}
- end
+ {cl, cl_extra} =
+ case {Util.float_paparse(cl), cl} do
+ {{cl, extra}, _} ->
+ {cl, extra}
+
+ {:error, "(" <> _} ->
+ try do
+ {:ok, result} = Abacus.eval(cl)
+ {result, nil}
+ rescue
+ _ -> {nil, "cl: invalid calc expression"}
+ end
- {deg, comment, auto_set, beer_id} = case Util.float_paparse(deg) do
- {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
- {:ok, %{"response" => %{"beers" => %{"count" => count, "items" => [%{"beer" => beer, "brewery" => brewery} | _]}}}} ->
- {Map.get(beer, "beer_abv"), "#{Map.get(brewery, "brewery_name")}: #{Map.get(beer, "beer_name")}", true, Map.get(beer, "bid")}
- _ ->
+ {:error, _} ->
+ {nil, "cl: invalid value"}
+ end
+
+ {deg, comment, auto_set, beer_id} =
+ case Util.float_paparse(deg) do
+ {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
+ {:ok,
+ %{
+ "response" => %{
+ "beers" => %{
+ "count" => count,
+ "items" => [%{"beer" => beer, "brewery" => brewery} | _]
+ }
+ }
+ }} ->
+ {Map.get(beer, "beer_abv"),
+ "#{Map.get(brewery, "brewery_name")}: #{Map.get(beer, "beer_name")}", true,
+ Map.get(beer, "bid")}
+
+ _ ->
{deg, "could not find beer", false, nil}
- end
- end
+ end
+ end
cond do
- cl == nil -> m.replyfun.(cl_extra)
- deg == nil -> m.replyfun.(comment)
- cl >= 500 || deg >= 100 -> Nola.Plugins.Txt.reply_random(m, "alcoolog.drink_toohuge")
- cl == 0 || deg == 0 -> Nola.Plugins.Txt.reply_random(m, "alcoolog.drink_zero")
- cl < 0 || deg < 0 -> Nola.Plugins.Txt.reply_random(m, "alcoolog.drink_negative")
+ cl == nil ->
+ m.replyfun.(cl_extra)
+
+ deg == nil ->
+ m.replyfun.(comment)
+
+ cl >= 500 || deg >= 100 ->
+ Nola.Plugins.Txt.reply_random(m, "alcoolog.drink_toohuge")
+
+ cl == 0 || deg == 0 ->
+ Nola.Plugins.Txt.reply_random(m, "alcoolog.drink_zero")
+
+ cl < 0 || deg < 0 ->
+ Nola.Plugins.Txt.reply_random(m, "alcoolog.drink_negative")
+
true ->
points = Alcool.units(cl, deg)
- now = m.at || DateTime.utc_now()
- |> DateTime.to_unix(: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)
@@ -344,87 +493,132 @@ defmodule Nola.Plugins.Alcoolog do
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()
+
+ :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()
sante = Nola.Plugins.Txt.random("alcoolog.santai")
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)
+ peak = Float.round((10 * points || 0.0) / (k * weight), 4)
stats = get_full_statistics(state, m.account.id)
- sober_add = if old_stats && Map.get(old_stats || %{}, :sober_in) do
- mins = round(stats.sober_in - old_stats.sober_in)
- " [+#{mins}m]"
- else
- ""
- end
+
+ sober_add =
+ if old_stats && Map.get(old_stats || %{}, :sober_in) do
+ mins = round(stats.sober_in - old_stats.sober_in)
+ " [+#{mins}m]"
+ else
+ ""
+ end
+
nonow = DateTime.utc_now()
- sober = nonow |> DateTime.add(round(stats.sober_in*60), :second)
- |> Timex.Timezone.convert("Europe/Paris")
- at = if nonow.day == sober.day do
- {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "aujourd'hui {h24}:{m}", "fr")
- detail
- else
- {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr")
- detail
- end
- up = if stats.active_drinks > 1 do
- " " <> Enum.join(for(_ <- 1..stats.active_drinks, do: "▲")) <> ""
- else
- ""
- end
+ sober =
+ nonow
+ |> DateTime.add(round(stats.sober_in * 60), :second)
+ |> Timex.Timezone.convert("Europe/Paris")
- since_str = if stats.since && stats.since_min > 180 do
- "(depuis: #{stats.since_s}) "
- else
- ""
- end
+ at =
+ if nonow.day == sober.day do
+ {:ok, detail} =
+ Timex.Format.DateTime.Formatters.Default.lformat(
+ sober,
+ "aujourd'hui {h24}:{m}",
+ "fr"
+ )
- 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}) #{since_str}(sobriété #{at} (dans #{stats.sober_in_s})#{sober_add}) !"
- <> " (aujourd'hui #{stats.daily_volumes} points - #{stats.daily_gl} g/l)"
- end
+ detail
+ else
+ {:ok, detail} =
+ Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr")
- meta = if beer_id do
- Map.put(meta, "untappd:beer_id", beer_id)
- else
- meta
+ detail
+ end
+
+ up =
+ if stats.active_drinks > 1 do
+ " " <> Enum.join(for(_ <- 1..stats.active_drinks, do: "▲")) <> ""
+ else
+ ""
+ 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}) #{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() ->
+ spawn(fn ->
case Untappd.maybe_checkin(m.account, beer_id) do
{:ok, body} ->
badges = get_in(body, ["badges", "items"])
+
if badges != [] do
- badges_s = Enum.map(badges, fn(badge) -> Map.get(badge, "badge_name") end)
- |> Enum.filter(fn(b) -> b end)
- |> Enum.intersperse(", ")
- |> Enum.join("")
- badge = if(length(badges) > 1, do: "badges", else: "badge")
- m.replyfun.("\\O/ Unlocked untappd #{badge}: #{badges_s}")
+ badges_s =
+ Enum.map(badges, fn badge -> Map.get(badge, "badge_name") end)
+ |> Enum.filter(fn b -> b end)
+ |> Enum.intersperse(", ")
+ |> Enum.join("")
+
+ badge = if(length(badges) > 1, do: "badges", else: "badge")
+ m.replyfun.("\\O/ Unlocked untappd #{badge}: #{badges_s}")
end
+
:ok
- {:error, {:http_error, error}} when is_integer(error) -> m.replyfun.("Checkin to Untappd failed: #{to_string(error)}")
- {:error, {:http_error, error}} -> m.replyfun.("Checkin to Untappd failed: #{inspect error}")
- _ -> :error
+
+ {:error, {:http_error, error}} when is_integer(error) ->
+ m.replyfun.("Checkin to Untappd failed: #{to_string(error)}")
+
+ {:error, {:http_error, error}} ->
+ m.replyfun.("Checkin to Untappd failed: #{inspect(error)}")
+
+ _ ->
+ :error
end
end)
end
- local_extra = if auto_set do
- if comment do
- " #{comment} (#{cl}cl @ #{deg}°)"
+ local_extra =
+ if auto_set do
+ if comment do
+ " #{comment} (#{cl}cl @ #{deg}°)"
+ else
+ "#{cl}cl @ #{deg}°"
+ end
else
- "#{cl}cl @ #{deg}°"
+ ""
end
- else
- ""
- end
+
m.replyfun.(msg.(m.sender.nick, local_extra))
- notify = Nola.Membership.notify_channels(m.account) -- [{m.network,m.channel}]
+ notify = Nola.Membership.notify_channels(m.account) -- [{m.network, m.channel}]
+
for {net, chan} <- notify do
user = Nola.UserTrack.find_by_account(net, m.account)
nick = if(user, do: user.nick, else: m.account.name)
@@ -432,24 +626,26 @@ defmodule Nola.Plugins.Alcoolog do
Nola.Irc.Connection.broadcast_message(net, chan, msg.(nick, extra))
end
- miss = cond do
- points <= 0.6 -> :small
- stats.active30m >= 2.9 && stats.active30m < 3 -> :miss3
- stats.active30m >= 1.9 && stats.active30m < 2 -> :miss2
- stats.active30m >= 0.9 && stats.active30m < 1 -> :miss1
- stats.active30m >= 0.45 && stats.active30m < 0.5 -> :miss05
- stats.active30m >= 0.20 && stats.active30m < 0.20 -> :miss025
- stats.active30m >= 3 && stats.active1h < 3.15 -> :small3
- stats.active30m >= 2 && stats.active1h < 2.15 -> :small2
- stats.active30m >= 1.5 && stats.active1h < 1.5 -> :small15
- stats.active30m >= 1 && stats.active1h < 1.15 -> :small1
- stats.active30m >= 0.5 && stats.active1h <= 0.51 -> :small05
- stats.active30m >= 0.25 && stats.active30m <= 0.255 -> :small025
- true -> nil
- end
+ miss =
+ cond do
+ points <= 0.6 -> :small
+ stats.active30m >= 2.9 && stats.active30m < 3 -> :miss3
+ stats.active30m >= 1.9 && stats.active30m < 2 -> :miss2
+ stats.active30m >= 0.9 && stats.active30m < 1 -> :miss1
+ stats.active30m >= 0.45 && stats.active30m < 0.5 -> :miss05
+ stats.active30m >= 0.20 && stats.active30m < 0.20 -> :miss025
+ stats.active30m >= 3 && stats.active1h < 3.15 -> :small3
+ stats.active30m >= 2 && stats.active1h < 2.15 -> :small2
+ stats.active30m >= 1.5 && stats.active1h < 1.5 -> :small15
+ stats.active30m >= 1 && stats.active1h < 1.15 -> :small1
+ stats.active30m >= 0.5 && stats.active1h <= 0.51 -> :small05
+ stats.active30m >= 0.25 && stats.active30m <= 0.255 -> :small025
+ true -> nil
+ end
if miss do
miss = Nola.Plugins.Txt.random("alcoolog.#{to_string(miss)}")
+
if miss do
for {net, chan} <- Nola.Membership.notify_channels(m.account) do
user = Nola.UserTrack.find_by_account(net, m.account)
@@ -461,45 +657,59 @@ defmodule Nola.Plugins.Alcoolog do
end
end
- def handle_info({:irc, :trigger, "santai", m = %Nola.Message{trigger: %Nola.Trigger{args: _, type: :bang}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "santai",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: _, type: :bang}}},
+ state
+ ) do
m.replyfun.("!santai <cl> <degrés> [commentaire]")
{:noreply, state}
end
def get_all_stats() do
Nola.Account.all_accounts()
- |> Enum.map(fn(account) -> {account.id, get_full_statistics(account.id)} end)
- |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end)
- |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2)
+ |> Enum.map(fn account -> {account.id, get_full_statistics(account.id)} end)
+ |> Enum.filter(fn {_nick, status} ->
+ status && (status.active > 0 || status.active30m > 0)
+ end)
+ |> Enum.sort_by(fn {_, status} -> status.active end, &>/2)
end
def get_channel_statistics(account, network, nil) do
Nola.Membership.expanded_members_or_friends(account, network, nil)
- |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(account.id)} end)
- |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end)
- |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2)
+ |> Enum.map(fn {account, _, nick} -> {nick, get_full_statistics(account.id)} end)
+ |> Enum.filter(fn {_nick, status} ->
+ status && (status.active > 0 || status.active30m > 0)
+ end)
+ |> Enum.sort_by(fn {_, status} -> status.active end, &>/2)
end
def get_channel_statistics(_, network, channel), do: get_channel_statistics(network, channel)
def get_channel_statistics(network, channel) do
Nola.Membership.expanded_members(network, channel)
- |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(account.id)} end)
- |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end)
- |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2)
+ |> Enum.map(fn {account, _, nick} -> {nick, get_full_statistics(account.id)} end)
+ |> Enum.filter(fn {_nick, status} ->
+ status && (status.active > 0 || status.active30m > 0)
+ end)
+ |> Enum.sort_by(fn {_, status} -> status.active end, &>/2)
end
@spec since() :: %{Nola.Account.id() => DateTime.t()}
@doc "Returns the last time the user was at 0 g/l"
def since() do
- :ets.foldr(fn({{acct, timestamp_or_date}, _vol, current, _cl, _deg, _name, _comment, _m}, acc) ->
- if !Map.get(acc, acct) && current == 0 do
- date = Util.to_date_time(timestamp_or_date)
- Map.put(acc, acct, date)
- else
- acc
- end
- end, %{}, __MODULE__.ETS)
+ :ets.foldr(
+ fn {{acct, timestamp_or_date}, _vol, current, _cl, _deg, _name, _comment, _m}, acc ->
+ if !Map.get(acc, acct) && current == 0 do
+ date = Util.to_date_time(timestamp_or_date)
+ Map.put(acc, acct, date)
+ else
+ acc
+ end
+ end,
+ %{},
+ __MODULE__.ETS
+ )
end
def get_full_statistics(nick) do
@@ -508,129 +718,204 @@ defmodule Nola.Plugins.Alcoolog do
defp get_full_statistics(state, nick) do
case get_statistics_for_nick(state, nick) do
- {count, {_, last_at, last_points, last_active, last_cl, last_deg, last_type, last_descr, _meta}} ->
+ {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)
{_, m5} = alcohol_level_rising(state, nick, 5)
{_, h1} = alcohol_level_rising(state, nick, 60)
- trend = if rising do
- "▲"
- else
- "▼"
- end
- user_state = cond do
- active <= 0.0 -> :sober
- active <= 0.25 -> :low
- active <= 0.50 -> :legal
- active <= 1.0 -> :legalhigh
- active <= 2.5 -> :high
- active < 3 -> :toohigh
- true -> :sick
- end
+ trend =
+ if rising do
+ "▲"
+ else
+ "▼"
+ end
- rising_file_key = if rising, do: "_rising", else: ""
- txt_file = "alcoolog." <> "user_" <> to_string(user_state) <> rising_file_key
- user_status = Nola.Plugins.Txt.random(txt_file)
-
- meta = get_user_meta(state, nick)
- minutes_til_sober = h1/((meta.loss_factor/100)/60)
- minutes_til_sober = cond do
- active < 0 -> 0
- m15 < 0 -> 15
- m30 < 0 -> 30
- h1 < 0 -> 60
- minutes_til_sober > 0 ->
- Float.round(minutes_til_sober+60)
- true -> 0
- end
+ user_state =
+ cond do
+ active <= 0.0 -> :sober
+ active <= 0.25 -> :low
+ active <= 0.50 -> :legal
+ active <= 1.0 -> :legalhigh
+ active <= 2.5 -> :high
+ active < 3 -> :toohigh
+ true -> :sick
+ end
+
+ rising_file_key = if rising, do: "_rising", else: ""
+ txt_file = "alcoolog." <> "user_" <> to_string(user_state) <> rising_file_key
+ user_status = Nola.Plugins.Txt.random(txt_file)
+
+ meta = get_user_meta(state, nick)
+ minutes_til_sober = h1 / (meta.loss_factor / 100 / 60)
+
+ minutes_til_sober =
+ cond do
+ active < 0 ->
+ 0
+
+ m15 < 0 ->
+ 15
+
+ m30 < 0 ->
+ 30
+
+ h1 < 0 ->
+ 60
+
+ minutes_til_sober > 0 ->
+ Float.round(minutes_til_sober + 60)
+
+ true ->
+ 0
+ end
duration = Timex.Duration.from_minutes(minutes_til_sober)
- sober_in_s = if minutes_til_sober > 0 do
- Timex.Format.Duration.Formatter.lformat(duration, "fr", :humanized)
- else
- nil
- end
- since = if active > 0 do
- since()
- |> Map.get(nick)
- end
+ sober_in_s =
+ if minutes_til_sober > 0 do
+ Timex.Format.Duration.Formatter.lformat(duration, "fr", :humanized)
+ else
+ 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)
- {total_volumes, total_gl} = user_stats(state, nick)
+ since_s =
+ if since, do: Timex.Format.Duration.Formatter.lformat(since_duration, "fr", :humanized)
+ {total_volumes, total_gl} = user_stats(state, nick)
- %{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,
+ %{
+ 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,
+ 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,
- since: since, since_min: since_diff, since_s: since_s,
+ daily_gl: total_gl,
+ daily_volumes: total_volumes,
+ sober_in: minutes_til_sober,
+ sober_in_s: sober_in_s,
+ since: since,
+ since_min: since_diff,
+ since_s: since_s
}
- _ ->
- nil
+
+ _ ->
+ nil
end
end
- def handle_info({:irc, :trigger, "sobre", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :dot}}}, state) do
- nicks = Nola.Membership.expanded_members_or_friends(m.account, m.network, m.channel)
- |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(state, account.id)} end)
- |> Enum.filter(fn({_nick, status}) -> status && status.sober_in && status.sober_in > 0 end)
- |> Enum.sort_by(fn({_, status}) -> status.sober_in end, &</2)
- |> Enum.map(fn({nick, stats}) ->
- now = DateTime.utc_now()
- sober = now |> DateTime.add(round(stats.sober_in*60), :second)
- |> Timex.Timezone.convert("Europe/Paris")
- at = if now.day == sober.day do
- {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "aujourd'hui {h24}:{m}", "fr")
- detail
- else
- {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr")
- detail
- end
- "#{nick} sobre #{at} (dans #{stats.sober_in_s})"
- end)
- |> Enum.intersperse(", ")
- |> Enum.join("")
- |> (fn(line) ->
- case line do
- "" -> "tout le monde est sobre......."
- line -> line
- end
- end).()
- |> m.replyfun.()
+ def handle_info(
+ {:irc, :trigger, "sobre",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :dot}}},
+ state
+ ) do
+ nicks =
+ Nola.Membership.expanded_members_or_friends(m.account, m.network, m.channel)
+ |> Enum.map(fn {account, _, nick} -> {nick, get_full_statistics(state, account.id)} end)
+ |> Enum.filter(fn {_nick, status} -> status && status.sober_in && status.sober_in > 0 end)
+ |> Enum.sort_by(fn {_, status} -> status.sober_in end, &</2)
+ |> Enum.map(fn {nick, stats} ->
+ now = DateTime.utc_now()
+
+ sober =
+ now
+ |> DateTime.add(round(stats.sober_in * 60), :second)
+ |> Timex.Timezone.convert("Europe/Paris")
+
+ at =
+ if now.day == sober.day do
+ {:ok, detail} =
+ Timex.Format.DateTime.Formatters.Default.lformat(
+ sober,
+ "aujourd'hui {h24}:{m}",
+ "fr"
+ )
+
+ detail
+ else
+ {:ok, detail} =
+ Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr")
+
+ detail
+ end
+
+ "#{nick} sobre #{at} (dans #{stats.sober_in_s})"
+ end)
+ |> Enum.intersperse(", ")
+ |> Enum.join("")
+ |> (fn line ->
+ case line do
+ "" -> "tout le monde est sobre......."
+ line -> line
+ end
+ end).()
+ |> m.replyfun.()
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "sobre", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}}, state) do
- account = case args do
- [nick] -> Nola.Account.find_always_by_nick(m.network, m.channel, nick)
- [] -> m.account
- end
+ def handle_info(
+ {:irc, :trigger, "sobre",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}},
+ state
+ ) do
+ account =
+ case args do
+ [nick] -> Nola.Account.find_always_by_nick(m.network, m.channel, nick)
+ [] -> m.account
+ end
if account do
user = Nola.UserTrack.find_by_account(m.network, account)
nick = if(user, do: user.nick, else: account.name)
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)
- |> Timex.Timezone.convert("Europe/Paris")
- at = if now.day == sober.day do
- {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "aujourd'hui {h24}:{m}", "fr")
+
+ sober =
+ now
+ |> DateTime.add(round(stats.sober_in * 60), :second)
+ |> Timex.Timezone.convert("Europe/Paris")
+
+ at =
+ if now.day == sober.day do
+ {:ok, detail} =
+ Timex.Format.DateTime.Formatters.Default.lformat(
+ sober,
+ "aujourd'hui {h24}:{m}",
+ "fr"
+ )
+
detail
else
- {:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr")
+ {:ok, detail} =
+ Timex.Format.DateTime.Formatters.Default.lformat(sober, "{WDfull} {h24}:{m}", "fr")
+
detail
end
+
m.replyfun.("#{nick} sera sobre #{at} (dans #{stats.sober_in_s})!")
else
m.replyfun.("#{nick} est déjà sobre. aidez le !")
@@ -638,61 +923,85 @@ defmodule Nola.Plugins.Alcoolog do
else
m.replyfun.("inconnu")
end
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "alcoolisme", m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :dot}}}, state) do
- nicks = Nola.Membership.expanded_members_or_friends(m.account, m.network, m.channel)
- |> Enum.map(fn({account, _, nick}) -> {nick, get_full_statistics(state, account.id)} end)
- |> Enum.filter(fn({_nick, status}) -> status && (status.active > 0 || status.active30m > 0) end)
- |> Enum.sort_by(fn({_, status}) -> status.active end, &>/2)
- |> Enum.map(fn({nick, status}) ->
- trend_symbol = if status.active_drinks > 1 do
- Enum.join(for(_ <- 1..status.active_drinks, do: status.trend_symbol))
- else
- status.trend_symbol
- end
- since_str = if status.since_min > 180 do
- "depuis: #{status.since_s} | "
+ def handle_info(
+ {:irc, :trigger, "alcoolisme",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :dot}}},
+ state
+ ) do
+ nicks =
+ Nola.Membership.expanded_members_or_friends(m.account, m.network, m.channel)
+ |> Enum.map(fn {account, _, nick} -> {nick, get_full_statistics(state, account.id)} end)
+ |> Enum.filter(fn {_nick, status} ->
+ status && (status.active > 0 || status.active30m > 0)
+ end)
+ |> Enum.sort_by(fn {_, status} -> status.active end, &>/2)
+ |> Enum.map(fn {nick, status} ->
+ trend_symbol =
+ if status.active_drinks > 1 do
+ Enum.join(for(_ <- 1..status.active_drinks, do: status.trend_symbol))
+ else
+ status.trend_symbol
+ end
+
+ 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("")
+
+ msg =
+ if nicks == "" do
+ "wtf?!?! personne n'a bu!"
else
- ""
+ nicks
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("")
-
- msg = if nicks == "" do
- "wtf?!?! personne n'a bu!"
- else
- nicks
- end
m.replyfun.(msg)
{:noreply, state}
end
- def handle_info({:irc, :trigger, "alcoolisme", m = %Nola.Message{trigger: %Nola.Trigger{args: [time], type: :dot}}}, state) do
- time = case time do
- "semaine" -> 7
- string ->
- case Integer.parse(string) do
- {time, "j"} -> time
- {time, "J"} -> time
- _ -> nil
- end
- end
+ def handle_info(
+ {:irc, :trigger, "alcoolisme",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [time], type: :dot}}},
+ state
+ ) do
+ time =
+ case time do
+ "semaine" ->
+ 7
+
+ string ->
+ case Integer.parse(string) do
+ {time, "j"} -> time
+ {time, "J"} -> time
+ _ -> nil
+ end
+ end
if time do
- aday = time*((24 * 60)*60)
+ aday = time * (24 * 60 * 60)
now = DateTime.utc_now()
- before = now
- |> DateTime.add(-aday, :second)
- |> DateTime.to_unix(:millisecond)
+
+ before =
+ now
+ |> DateTime.add(-aday, :second)
+ |> DateTime.to_unix(:millisecond)
+
over_time_stats(before, time, m, state)
else
m.replyfun.(".alcooolisme semaine|Xj")
end
+
{:noreply, state}
end
@@ -701,30 +1010,37 @@ defmodule Nola.Plugins.Alcoolog do
end
def user_over_time(state, account, count) do
- delay = count*((24 * 60)*60)
+ delay = count * (24 * 60 * 60)
now = DateTime.utc_now()
- before = DateTime.utc_now()
- |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
- |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
- |> DateTime.to_unix(:millisecond)
- #[
-# {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
-# [{:andalso, {:==, :"$1", :"$1"}, {:<, :"$2", {:const, 3000}}}], [:lol]}
- #]
- match = [{{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
+
+ before =
+ DateTime.utc_now()
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
+ |> DateTime.to_unix(:millisecond)
+
+ # [
+ # {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
+ # [{:andalso, {:==, :"$1", :"$1"}, {:<, :"$2", {:const, 3000}}}], [:lol]}
+ # ]
+ match = [
+ {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
[{:andalso, {:>, :"$2", {:const, before}}, {:==, :"$1", {:const, account.id}}}], [:"$_"]}
]
- :ets.select(state.ets, match)
- |> Enum.reduce(Map.new, fn({{_, ts}, vol, _, _, _, _, _, _}, acc) ->
- date = DateTime.from_unix!(ts, :millisecond)
- |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
- date = if date.hour <= 8 do
- DateTime.add(date, -(60*(60*(date.hour+1))), :second, Tzdata.TimeZoneDatabase)
- else
- date
- end
- |> DateTime.to_date()
+ :ets.select(state.ets, match)
+ |> Enum.reduce(Map.new(), fn {{_, ts}, vol, _, _, _, _, _, _}, acc ->
+ date =
+ DateTime.from_unix!(ts, :millisecond)
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+
+ date =
+ if date.hour <= 8 do
+ DateTime.add(date, -(60 * (60 * (date.hour + 1))), :second, Tzdata.TimeZoneDatabase)
+ else
+ date
+ end
+ |> DateTime.to_date()
Map.put(acc, date, Map.get(acc, date, 0) + vol)
end)
@@ -733,164 +1049,219 @@ defmodule Nola.Plugins.Alcoolog do
def user_over_time_gl(account, count) do
state = data_state()
meta = get_user_meta(state, account.id)
- delay = count*((24 * 60)*60)
+ delay = count * (24 * 60 * 60)
now = DateTime.utc_now()
- before = DateTime.utc_now()
- |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
- |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
- |> DateTime.to_unix(:millisecond)
- #[
-# {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
-# [{:andalso, {:==, :"$1", :"$1"}, {:<, :"$2", {:const, 3000}}}], [:lol]}
- #]
- match = [{{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
+
+ before =
+ DateTime.utc_now()
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ |> DateTime.add(-delay, :second, Tzdata.TimeZoneDatabase)
+ |> DateTime.to_unix(:millisecond)
+
+ # [
+ # {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
+ # [{:andalso, {:==, :"$1", :"$1"}, {:<, :"$2", {:const, 3000}}}], [:lol]}
+ # ]
+ match = [
+ {{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
[{:andalso, {:>, :"$2", {:const, before}}, {:==, :"$1", {:const, account.id}}}], [:"$_"]}
]
+
:ets.select(state.ets, match)
- |> Enum.reduce(Map.new, fn({{_, ts}, vol, _, _, _, _, _, _}, acc) ->
- date = DateTime.from_unix!(ts, :millisecond)
- |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ |> 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()
- date = if date.hour <= 8 do
- DateTime.add(date, -(60*(60*(date.hour+1))), :second, Tzdata.TimeZoneDatabase)
- else
- date
- end
- |> DateTime.to_date()
weight = meta.weight
k = if meta.sex, do: 0.7, else: 0.6
- gl = (10*vol)/(k*weight)
+ gl = 10 * vol / (k * weight)
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"}, :_, :_, :_, :_, :_, :_, :_},
- [{:>, :"$1", {:const, before}}], [:"$_"]}
+ # 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}
+
+ # tuple ets: {{nick, date}, volumes, current, nom, commentaire}
members = Nola.Membership.members_or_friends(m.account, m.network, m.channel)
- drinks = :ets.select(state.ets, match)
- |> Enum.filter(fn({{account, _}, _, _, _, _, _, _, _}) -> Enum.member?(members, account) end)
- |> Enum.sort_by(fn({{_, ts}, _, _, _, _, _, _, _}) -> ts end, &>/2)
- top = Enum.reduce(drinks, %{}, fn({{nick, _}, vol, _, _, _, _, _, _}, acc) ->
- all = Map.get(acc, nick, 0)
- Map.put(acc, nick, all + vol)
- end)
- |> Enum.sort_by(fn({_nick, count}) -> count end, &>/2)
- |> Enum.map(fn({nick, count}) ->
- account = Nola.Account.get(nick)
- user = Nola.UserTrack.find_by_account(m.network, account)
- nick = if(user, do: user.nick, else: account.name)
- "#{nick}: #{Float.round(count, 4)}"
- end)
- |> Enum.intersperse(", ")
+ drinks =
+ :ets.select(state.ets, match)
+ |> Enum.filter(fn {{account, _}, _, _, _, _, _, _, _} -> Enum.member?(members, account) end)
+ |> Enum.sort_by(fn {{_, ts}, _, _, _, _, _, _, _} -> ts end, &>/2)
+
+ top =
+ Enum.reduce(drinks, %{}, fn {{nick, _}, vol, _, _, _, _, _, _}, acc ->
+ all = Map.get(acc, nick, 0)
+ Map.put(acc, nick, all + vol)
+ end)
+ |> Enum.sort_by(fn {_nick, count} -> count end, &>/2)
+ |> Enum.map(fn {nick, count} ->
+ account = Nola.Account.get(nick)
+ user = Nola.UserTrack.find_by_account(m.network, account)
+ nick = if(user, do: user.nick, else: account.name)
+ "#{nick}: #{Float.round(count, 4)}"
+ end)
+ |> Enum.intersperse(", ")
m.replyfun.("sur #{j} jours: #{top}")
{:noreply, state}
end
- def handle_info({:irc, :trigger, "alcoolisme", m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :plus}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "alcoolisme",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :plus}}},
+ state
+ ) do
meta = get_user_meta(state, m.account.id)
hf = if meta.sex, do: "h", else: "f"
- m.replyfun.("+alcoolisme sexe: #{hf} poids: #{meta.weight} facteur de perte: #{meta.loss_factor}")
+
+ m.replyfun.(
+ "+alcoolisme sexe: #{hf} poids: #{meta.weight} facteur de perte: #{meta.loss_factor}"
+ )
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "alcoolisme", m = %Nola.Message{trigger: %Nola.Trigger{args: [h, weight | rest], type: :plus}}}, state) do
- h = case h do
- "h" -> true
- "f" -> false
- _ -> nil
- end
+ def handle_info(
+ {:irc, :trigger, "alcoolisme",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [h, weight | rest], type: :plus}}},
+ state
+ ) do
+ h =
+ case h do
+ "h" -> true
+ "f" -> false
+ _ -> nil
+ end
- weight = case Util.float_paparse(weight) do
+ weight =
+ case Util.float_paparse(weight) do
{weight, _} -> weight
_ -> nil
end
- {factor} = case rest do
+ {factor} =
+ case rest do
[factor] ->
case Util.float_paparse(factor) do
{float, _} -> {float}
_ -> {@default_user_meta.loss_factor}
end
- _ -> {@default_user_meta.loss_factor}
+
+ _ ->
+ {@default_user_meta.loss_factor}
end
- if h == nil || weight == nil do
- m.replyfun.("paramètres invalides")
- else
- old_meta = get_user_meta(state, m.account.id)
- meta = Map.merge(@default_user_meta, %{sex: h, weight: weight, loss_factor: factor})
- put_user_meta(state, m.account.id, meta)
- cond do
- old_meta.weight < meta.weight ->
- Nola.Plugins.Txt.reply_random(m, "alcoolog.fatter")
- old_meta.weight == meta.weight ->
- m.replyfun.("aucun changement!")
- true ->
- Nola.Plugins.Txt.reply_random(m, "alcoolog.thinner")
- end
+ if h == nil || weight == nil do
+ m.replyfun.("paramètres invalides")
+ else
+ old_meta = get_user_meta(state, m.account.id)
+ meta = Map.merge(@default_user_meta, %{sex: h, weight: weight, loss_factor: factor})
+ put_user_meta(state, m.account.id, meta)
+
+ cond do
+ old_meta.weight < meta.weight ->
+ Nola.Plugins.Txt.reply_random(m, "alcoolog.fatter")
+
+ old_meta.weight == meta.weight ->
+ m.replyfun.("aucun changement!")
+
+ true ->
+ Nola.Plugins.Txt.reply_random(m, "alcoolog.thinner")
end
+ end
{:noreply, state}
end
- def handle_info({:irc, :trigger, "santai", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :minus}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "santai",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :minus}}},
+ state
+ ) do
case get_statistics_for_nick(state, m.account.id) do
{_, 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}")
Nola.Plugins.Txt.reply_random(m, "alcoolog.delete")
- notify = Nola.Membership.notify_channels(m.account) -- [{m.network,m.channel}]
+ notify = Nola.Membership.notify_channels(m.account) -- [{m.network, m.channel}]
+
for {net, chan} <- notify do
user = Nola.UserTrack.find_by_account(net, m.account)
nick = if(user, do: user.nick, else: m.account.name)
- Nola.Irc.Connection.broadcast_message(net, chan, "#{nick} -santai #{points} #{type} #{descr}")
+
+ Nola.Irc.Connection.broadcast_message(
+ net,
+ chan,
+ "#{nick} -santai #{points} #{type} #{descr}"
+ )
end
+
{:noreply, state}
+
_ ->
{:noreply, state}
end
end
+ def handle_info(
+ {:irc, :trigger, "alcoolisme",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}},
+ state
+ ) do
+ {account, duration} =
+ case args do
+ [nick | rest] -> {Nola.Account.find_always_by_nick(m.network, m.channel, nick), rest}
+ [] -> {m.account, []}
+ end
- def handle_info({:irc, :trigger, "alcoolisme", m = %Nola.Message{trigger: %Nola.Trigger{args: args, type: :bang}}}, state) do
- {account, duration} = case args do
- [nick | rest] -> {Nola.Account.find_always_by_nick(m.network, m.channel, nick), rest}
- [] -> {m.account, []}
- end
if account do
- duration = case duration do
- ["semaine"] -> 7
- [j] ->
- case Integer.parse(j) do
- {j, "j"} -> j
- _ -> nil
- end
- _ -> nil
- end
+ duration =
+ case duration do
+ ["semaine"] ->
+ 7
+
+ [j] ->
+ case Integer.parse(j) do
+ {j, "j"} -> j
+ _ -> nil
+ end
+
+ _ ->
+ nil
+ end
+
user = Nola.UserTrack.find_by_account(m.network, account)
nick = if(user, do: user.nick, else: account.name)
+
if duration do
if duration > 90 do
m.replyfun.("trop gros, ça rentrera pas")
else
# duration stats
- stats = user_over_time(state, account, duration)
- |> Enum.sort_by(fn({k,_v}) -> k end, {:asc, Date})
- |> Enum.map(fn({date, count}) ->
- "#{date.day}: #{Float.round(count, 2)}"
- end)
- |> Enum.intersperse(", ")
- |> Enum.join("")
+ stats =
+ user_over_time(state, account, duration)
+ |> Enum.sort_by(fn {k, _v} -> k end, {:asc, Date})
+ |> Enum.map(fn {date, count} ->
+ "#{date.day}: #{Float.round(count, 2)}"
+ end)
+ |> Enum.intersperse(", ")
+ |> Enum.join("")
if stats == "" do
m.replyfun.("alcoolisme a zéro sur #{duration}j :/")
@@ -900,47 +1271,67 @@ defmodule Nola.Plugins.Alcoolog do
end
else
if stats = get_full_statistics(state, account.id) do
- trend_symbol = if stats.active_drinks > 1 do
- Enum.join(for(_ <- 1..stats.active_drinks, do: stats.trend_symbol))
- else
- stats.trend_symbol
- end
- # TODO: Lookup nick for account_id
- msg = "#{nick} #{stats.user_status} "
- <> (if stats.active > 0 || stats.active15m > 0 || stats.active30m > 0 || stats.active1h > 0, do: ": #{trend_symbol} #{Float.round(stats.active, 4)}g/l ", else: "")
- <> (if stats.active30m > 0 || stats.active1h > 0, do: "(15m: #{stats.active15m}, 30m: #{stats.active30m}, 1h: #{stats.active1h}) ", else: "")
- <> (if stats.sober_in > 0, do: "— Sobre dans #{stats.sober_in_s} ", else: "")
- <> (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: "")
+ trend_symbol =
+ if stats.active_drinks > 1 do
+ Enum.join(for(_ <- 1..stats.active_drinks, do: stats.trend_symbol))
+ else
+ stats.trend_symbol
+ end
+
+ # TODO: Lookup nick for account_id
+ msg =
+ "#{nick} #{stats.user_status} " <>
+ if(
+ stats.active > 0 || stats.active15m > 0 || stats.active30m > 0 ||
+ stats.active1h > 0,
+ do: ": #{trend_symbol} #{Float.round(stats.active, 4)}g/l ",
+ else: ""
+ ) <>
+ if(stats.active30m > 0 || stats.active1h > 0,
+ do: "(15m: #{stats.active15m}, 30m: #{stats.active30m}, 1h: #{stats.active1h}) ",
+ else: ""
+ ) <>
+ if(stats.sober_in > 0, do: "— Sobre dans #{stats.sober_in_s} ", else: "") <>
+ 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: ""
m.replyfun.(msg)
else
m.replyfun.("honteux mais #{nick} n'a pas l'air alcoolique du tout. /kick")
end
end
- else
+ else
m.replyfun.("je ne connais pas cet utilisateur")
end
+
{:noreply, state}
end
-
# Account merge
def handle_info({:account_change, old_id, new_id}, state) do
spec = [{{:"$1", :_, :_, :_, :_, :_, :_, :_, :_}, [{:==, :"$1", {:const, old_id}}], [:"$_"]}]
- Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
- Logger.debug("alcolog/account_change:: merging #{old_id} -> #{new_id}")
+
+ Util.ets_mutate_select_each(:dets, state.dets, spec, fn table, obj ->
+ Logger.debug("alcolog/account_change:: merging #{old_id} -> #{new_id}")
rename_object_owner(table, state.ets, obj, old_id, new_id)
end)
+
case :dets.lookup(state.meta, {:meta, old_id}) do
[{_, meta}] ->
:dets.delete(state.meta, {:meta, old_id})
:dets.insert(state.meta, {{:meta, new_id}, meta})
+
_ ->
:ok
end
+
{:noreply, state}
end
@@ -951,7 +1342,13 @@ defmodule Nola.Plugins.Alcoolog do
end
end
- defp rename_object_owner(table, ets, object = {old_id, date, volume, current, cl, deg, name, comment, meta}, 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, cl, deg, name, comment, meta})
@@ -960,58 +1357,75 @@ defmodule Nola.Plugins.Alcoolog do
# Account: move from nick to account id
def handle_info({:accounts, accounts}, state) do
- #for x={:account, _, _, _, _} <- accounts, do: handle_info(x, state)
- #{:noreply, state}
- mapping = Enum.reduce(accounts, Map.new, fn({:account, _net, _chan, nick, account_id}, acc) ->
- Map.put(acc, String.downcase(nick), account_id)
- end)
+ # for x={:account, _, _, _, _} <- accounts, do: handle_info(x, state)
+ # {:noreply, state}
+ mapping =
+ Enum.reduce(accounts, Map.new(), fn {:account, _net, _chan, nick, account_id}, acc ->
+ Map.put(acc, String.downcase(nick), account_id)
+ end)
+
spec = [{{:"$1", :_, :_, :_, :_, :_, :_, :_, :_}, [], [:"$_"]}]
- Logger.debug("accounts:: mappings #{inspect mapping}")
- Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj = {nick, _date, _vol, _cur, _cl, _deg, _name, _comment, _meta}) ->
- #Logger.debug("accounts:: item #{inspect(obj)}")
+ Logger.debug("accounts:: mappings #{inspect(mapping)}")
+
+ 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}")
rename_object_owner(table, state.ets, obj, nick, new_id)
end
end)
+
{:noreply, state}
end
def handle_info({:account, _net, _chan, nick, account_id}, state) do
nick = String.downcase(nick)
spec = [{{:"$1", :_, :_, :_, :_, :_, :_, :_, :_}, [{:==, :"$1", {:const, nick}}], [:"$_"]}]
- Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
+
+ Util.ets_mutate_select_each(:dets, state.dets, spec, fn table, obj ->
Logger.debug("alcoolog/account:: merging #{nick} -> #{account_id}")
rename_object_owner(table, state.ets, obj, nick, account_id)
end)
+
case :dets.lookup(state.meta, {:meta, nick}) do
[{_, meta}] ->
:dets.delete(state.meta, {:meta, nick})
:dets.insert(state.meta, {{:meta, account_id}, meta})
+
_ ->
:ok
end
+
{:noreply, state}
end
def handle_info(t, state) do
- Logger.debug("#{__MODULE__}: unhandled info #{inspect t}")
+ Logger.debug("#{__MODULE__}: unhandled info #{inspect(t)}")
{:noreply, state}
end
def nick_history(account) do
spec = [
- {{{:"$1", :_}, :_, :_, :_, :_, :_, :_, :_},
- [{:==, :"$1", {:const, account.id}}],
- [:"$_"]}
+ {{{:"$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, _cl, _deg, _type, _descr, _meta}, acc) -> acc + (points||0) end)
+ 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, _cl, _deg, _type, _descr, _meta},
+ acc ->
+ acc + (points || 0)
+ end)
+
last = List.last(qvc) || nil
{count, last}
end
@@ -1022,26 +1436,34 @@ defmodule Nola.Plugins.Alcoolog do
def format_points(int) when is_integer(int) and int > 0 do
"+#{Integer.to_string(int)}"
end
+
def format_points(int) when is_integer(int) and int < 0 do
Integer.to_string(int)
end
+
def format_points(int) when is_float(int) and int > 0 do
- "+#{Float.to_string(Float.round(int,4))}"
+ "+#{Float.to_string(Float.round(int, 4))}"
end
+
def format_points(int) when is_float(int) and int < 0 do
- Float.to_string(Float.round(int,4))
+ Float.to_string(Float.round(int, 4))
end
+
def format_points(0), do: "0"
def format_points(0.0), do: "0"
defp format_relative_timestamp(timestamp) do
alias Timex.Format.DateTime.Formatters
alias Timex.Timezone
- date = timestamp
- |> DateTime.from_unix!(:millisecond)
- |> Timezone.convert("Europe/Paris")
- {:ok, relative} = Formatters.Relative.relative_to(date, Timex.now("Europe/Paris"), "{relative}", "fr")
+ date =
+ timestamp
+ |> DateTime.from_unix!(:millisecond)
+ |> Timezone.convert("Europe/Paris")
+
+ {:ok, relative} =
+ Formatters.Relative.relative_to(date, Timex.now("Europe/Paris"), "{relative}", "fr")
+
{:ok, detail} = Formatters.Default.lformat(date, " ({h24}:{m})", "fr")
relative <> detail
@@ -1056,10 +1478,12 @@ defmodule Nola.Plugins.Alcoolog do
case :dets.lookup(meta, {:meta, account_id}) do
[{{:meta, _}, meta}] ->
Map.merge(@default_user_meta, meta)
+
_ ->
@default_user_meta
end
end
+
# Calcul g/l actuel:
# 1. load user meta
# 2. foldr ets
@@ -1077,12 +1501,15 @@ defmodule Nola.Plugins.Alcoolog do
defp user_stats(state = %{ets: ets}, account_id) do
meta = get_user_meta(state, account_id)
- aday = (10 * 60)*60
+ aday = 10 * 60 * 60
now = DateTime.utc_now()
- before = now
- |> DateTime.add(-aday, :second)
- |> DateTime.to_unix(:millisecond)
- #match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end)
+
+ before =
+ now
+ |> DateTime.add(-aday, :second)
+ |> DateTime.to_unix(:millisecond)
+
+ # match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end)
match = [
{{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
[
@@ -1090,43 +1517,57 @@ defmodule Nola.Plugins.Alcoolog do
{:"=:=", {:const, account_id}, :"$1"}
], [:"$_"]}
]
- # tuple ets: {{nick, date}, volumes, current, nom, commentaire}
+
+ # 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) ->
- acc + volume
- end)
+ total_volume =
+ Enum.reduce(drinks, 0.0, fn {{_, date}, volume, _, _, _, _, _, _}, acc ->
+ acc + volume
+ end)
+
k = if meta.sex, do: 0.7, else: 0.6
weight = meta.weight
- gl = (10*total_volume)/(k*weight)
+ gl = 10 * total_volume / (k * weight)
{Float.round(total_volume + 0.0, 4), Float.round(gl + 0.0, 4)}
end
defp alcohol_level_rising(state, account_id, minutes \\ 30) do
{now, _} = current_alcohol_level(state, account_id)
- soon_date = DateTime.utc_now
- |> DateTime.add(minutes*60, :second)
+
+ soon_date =
+ DateTime.utc_now()
+ |> DateTime.add(minutes * 60, :second)
+
{soon, _} = current_alcohol_level(state, account_id, soon_date)
- soon = cond do
- soon < 0 -> 0.0
- true -> soon
- end
- #IO.puts "soon #{soon_date} - #{inspect soon} #{inspect now}"
- {soon > now, Float.round(soon+0.0, 4)}
+
+ soon =
+ cond do
+ soon < 0 -> 0.0
+ true -> soon
+ end
+
+ # IO.puts "soon #{soon_date} - #{inspect soon} #{inspect now}"
+ {soon > now, Float.round(soon + 0.0, 4)}
end
defp current_alcohol_level(state = %{ets: ets}, account_id, now \\ nil) do
meta = get_user_meta(state, account_id)
- aday = ((24*7) * 60)*60
- now = if now do
+ aday = 24 * 7 * 60 * 60
+
+ now =
+ if now do
+ now
+ else
+ DateTime.utc_now()
+ end
+
+ before =
now
- else
- DateTime.utc_now()
- end
- before = now
- |> DateTime.add(-aday, :second)
- |> DateTime.to_unix(:millisecond)
- #match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end)
+ |> DateTime.add(-aday, :second)
+ |> DateTime.to_unix(:millisecond)
+
+ # match = :ets.fun2ms(fn(obj = {{^nick, date}, _, _, _, _}) when date > before -> obj end)
match = [
{{{:"$1", :"$2"}, :_, :_, :_, :_, :_, :_, :_},
[
@@ -1134,59 +1575,77 @@ defmodule Nola.Plugins.Alcoolog do
{:"=:=", {:const, account_id}, :"$1"}
], [:"$_"]}
]
- # tuple ets: {{nick, date}, volumes, current, nom, commentaire}
- drinks = :ets.select(ets, match)
- |> Enum.sort_by(fn({{_, date}, _, _, _, _, _, _, _}) -> date end, &</2)
+
+ # tuple ets: {{nick, date}, volumes, current, nom, commentaire}
+ drinks =
+ :ets.select(ets, match)
+ |> 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}) ->
- k = if meta.sex, do: 0.7, else: 0.6
- weight = meta.weight
- peak = (10*volume)/(k*weight)
- date = case date do
- ts when is_integer(ts) -> DateTime.from_unix!(ts, :millisecond)
- date = %NaiveDateTime{} -> DateTime.from_naive!(date, "Etc/UTC")
- date = %DateTime{} -> date
- end
- last_at = last_at || date
- mins_since = round(DateTime.diff(now, date)/60.0)
- #IO.puts "Drink: #{inspect({date, volume})} - mins since: #{inspect mins_since} - last drink at #{inspect last_at}"
- # Apply loss since `last_at` on `all`
- #
- all = if last_at do
- mins_since_last = round(DateTime.diff(date, last_at)/60.0)
- loss = ((meta.loss_factor/100)/60)*(mins_since_last)
- #IO.puts "Applying last drink loss: from #{all}, loss of #{inspect loss} (mins since #{inspect mins_since_first})"
- cond do
- (all-loss) > 0 -> all - loss
- true -> 0.0
+ {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)
+
+ date =
+ case date do
+ ts when is_integer(ts) -> DateTime.from_unix!(ts, :millisecond)
+ date = %NaiveDateTime{} -> DateTime.from_naive!(date, "Etc/UTC")
+ date = %DateTime{} -> date
+ end
+
+ last_at = last_at || date
+ mins_since = round(DateTime.diff(now, date) / 60.0)
+
+ # IO.puts "Drink: #{inspect({date, volume})} - mins since: #{inspect mins_since} - last drink at #{inspect last_at}"
+ # Apply loss since `last_at` on `all`
+ #
+ all =
+ if last_at do
+ mins_since_last = round(DateTime.diff(date, last_at) / 60.0)
+ loss = meta.loss_factor / 100 / 60 * mins_since_last
+
+ # IO.puts "Applying last drink loss: from #{all}, loss of #{inspect loss} (mins since #{inspect mins_since_first})"
+ cond do
+ all - loss > 0 -> all - loss
+ true -> 0.0
+ end
+ else
+ all
+ end
+
+ # IO.puts "Applying last drink current before drink: #{inspect all}"
+ if mins_since < 30 do
+ per_min = peak / 30.0
+ current = per_min * mins_since
+
+ # IO.puts "Applying current drink 30m: from #{peak}, loss of #{inspect per_min}/min (mins since #{inspect mins_since})"
+ {all + current, date, [{date, current} | acc], active_drinks + 1}
+ else
+ {all + peak, date, [{date, peak} | acc], active_drinks}
end
+ end)
+
+ # IO.puts "last drink #{inspect last_drink_at}"
+ mins_since_last =
+ if last_drink_at do
+ round(DateTime.diff(now, last_drink_at) / 60.0)
else
- all
+ 0
end
- #IO.puts "Applying last drink current before drink: #{inspect all}"
- if mins_since < 30 do
- per_min = (peak)/30.0
- current = (per_min*mins_since)
- #IO.puts "Applying current drink 30m: from #{peak}, loss of #{inspect per_min}/min (mins since #{inspect mins_since})"
- {all + current, date, [{date, current} | acc], active_drinks + 1}
+
+ # Si on a déjà bu y'a déjà moins 15 minutes (big up le binge drinking), on applique plus de perte
+ level =
+ if mins_since_last > 15 do
+ loss = meta.loss_factor / 100 / 60 * mins_since_last
+ Float.round(all - loss, 4)
else
- {all + peak, date, [{date, peak} | acc], active_drinks}
+ all
end
- end)
- #IO.puts "last drink #{inspect last_drink_at}"
- mins_since_last = if last_drink_at do
- round(DateTime.diff(now, last_drink_at)/60.0)
- else
- 0
- end
- # Si on a déjà bu y'a déjà moins 15 minutes (big up le binge drinking), on applique plus de perte
- level = if mins_since_last > 15 do
- loss = ((meta.loss_factor/100)/60)*(mins_since_last)
- Float.round(all - loss, 4)
- else
- all
- end
- #IO.puts "\n LEVEL #{inspect level}\n\n\n\n"
+
+ # IO.puts "\n LEVEL #{inspect level}\n\n\n\n"
cond do
level < 0 -> {0.0, 0}
true -> {level, active_drinks}
@@ -1194,23 +1653,31 @@ defmodule Nola.Plugins.Alcoolog do
end
defp format_duration_from_now(date, with_detail \\ true) do
- date = if is_integer(date) do
- date = DateTime.from_unix!(date, :millisecond)
+ date =
+ if is_integer(date) do
+ date =
+ DateTime.from_unix!(date, :millisecond)
+ |> Timex.Timezone.convert("Europe/Paris")
+ else
+ Util.to_naive_date_time(date)
+ end
+
+ now =
+ DateTime.utc_now()
|> Timex.Timezone.convert("Europe/Paris")
- else
- Util.to_naive_date_time(date)
- end
- now = DateTime.utc_now()
- |> Timex.Timezone.convert("Europe/Paris")
+
{:ok, detail} = Timex.Format.DateTime.Formatters.Default.lformat(date, "({h24}:{m})", "fr")
- mins_since = round(DateTime.diff(now, date)/60.0)
+ mins_since = round(DateTime.diff(now, date) / 60.0)
+
if ago = format_minute_duration(mins_since) do
- word = if mins_since > 0 do
- "il y a "
- else
- "dans "
- end
+ word =
+ if mins_since > 0 do
+ "il y a "
+ else
+ "dans "
+ end
+
word <> ago <> if(with_detail, do: " #{detail}", else: "")
else
"maintenant #{detail}"
@@ -1218,12 +1685,12 @@ defmodule Nola.Plugins.Alcoolog do
end
defp format_minute_duration(minutes) do
- sober_in_s = if (minutes != 0) do
- duration = Timex.Duration.from_minutes(minutes)
- Timex.Format.Duration.Formatter.lformat(duration, "fr", :humanized)
- else
- nil
- end
+ sober_in_s =
+ if minutes != 0 do
+ duration = Timex.Duration.from_minutes(minutes)
+ Timex.Format.Duration.Formatter.lformat(duration, "fr", :humanized)
+ else
+ nil
+ end
end
-
end
diff --git a/lib/plugins/alcoolog_announcer.ex b/lib/plugins/alcoolog_announcer.ex
index f172d85..452f56c 100644
--- a/lib/plugins/alcoolog_announcer.ex
+++ b/lib/plugins/alcoolog_announcer.ex
@@ -27,19 +27,21 @@ defmodule Nola.Plugins.AlcoologAnnouncer do
def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
def log(account) do
- dets_filename = (Nola.data_path() <> "/" <> "alcoologlog.dets") |> String.to_charlist
- {:ok, dets} = :dets.open_file(dets_filename, [{:type,:bag}])
+ dets_filename = (Nola.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)}}}
- ], [:"$_"]}
+ {{:"$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
@@ -49,25 +51,30 @@ defmodule Nola.Plugins.AlcoologAnnouncer do
{:ok, _} = Registry.register(Nola.PubSub, "account", [])
stats = get_stats()
Process.send_after(self(), :stats, :timer.seconds(30))
- dets_filename = (Nola.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, {stats, now(), dets, ets}}#, {:continue, :traverse}}
+ dets_filename = (Nola.data_path() <> "/" <> "alcoologlog.dets") |> String.to_charlist()
+ {:ok, dets} = :dets.open_file(dets_filename, [{:type, :bag}])
+
+ # :ets.new(__MODULE__.ETS, [:ordered_set, :named_table, :protected, {:read_concurrency, true}])
+ ets = nil
+ # , {:continue, :traverse}}
+ {:ok, {stats, now(), dets, ets}}
end
def handle_continue(:traverse, state = {_, _, dets, ets}) do
- traverse_fun = fn(obj, dets) ->
+ 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}")
+ IO.puts("ok #{inspect(obj)}")
dets
+
{nick, ts, value} ->
- :ets.insert(ets, { {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")
@@ -75,166 +82,210 @@ defmodule Nola.Plugins.AlcoologAnnouncer do
end
def alcohol_reached(old, new, level) do
- (old.active < level && new.active >= level) && (new.active5m >= level)
- end
+ 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
-
+ old.active > level && new.active <= level && new.active5m <= level
+ end
def handle_info(:stats, {old_stats, old_now, dets, ets}) do
stats = get_stats()
now = now()
if old_now.hour < 18 && now.hour == 18 do
- apero = Enum.shuffle(@apero)
- |> Enum.random()
+ apero =
+ Enum.shuffle(@apero)
+ |> Enum.random()
case apero do
{:timed, list} ->
- spawn(fn() ->
+ spawn(fn ->
for line <- list do
Nola.Irc.Connection.broadcast_message("evolu.net", "#dmz", line)
:timer.sleep(:timer.seconds(5))
end
end)
+
string ->
Nola.Irc.Connection.broadcast_message("evolu.net", "#dmz", string)
end
-
end
- #IO.puts "newstats #{inspect stats}"
- events = for {acct, old} <- old_stats 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, now, new[:active]})
- :ets.insert(ets, {{acct, now}, new[:active]})
- else
- :dets.insert(dets, {acct, now, 0.0})
- :ets.insert(ets, {{acct, now}, new[:active]})
- end
+ # IO.puts "newstats #{inspect stats}"
+ events =
+ for {acct, old} <- old_stats 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, now, new[:active]})
+ :ets.insert(ets, {{acct, now}, new[:active]})
+ else
+ :dets.insert(dets, {acct, now, 0.0})
+ :ets.insert(ets, {{acct, now}, new[:active]})
+ end
+
+ event =
+ cond do
+ old == nil -> nil
+ old.active > 0 && new == nil -> :sober
+ new == nil -> nil
+ alcohol_reached(old, new, 0.5) -> :stopconduire
+ alcohol_reached(old, new, 1.0) -> :g1
+ alcohol_reached(old, new, 2.0) -> :g2
+ alcohol_reached(old, new, 3.0) -> :g3
+ alcohol_reached(old, new, 4.0) -> :g4
+ alcohol_reached(old, new, 5.0) -> :g5
+ alcohol_reached(old, new, 6.0) -> :g6
+ alcohol_reached(old, new, 7.0) -> :g7
+ alcohol_reached(old, new, 10.0) -> :g10
+ alcohol_reached(old, new, 13.74) -> :record
+ alcohol_below(old, new, 0.5) -> :conduire
+ alcohol_below(old, new, 1.0) -> :fini1g
+ alcohol_below(old, new, 2.0) -> :fini2g
+ alcohol_below(old, new, 3.0) -> :fini3g
+ alcohol_below(old, new, 4.0) -> :fini4g
+ old.rising && !new.rising -> :lowering
+ true -> nil
+ end
- event = cond do
- old == nil -> nil
- (old.active > 0) && (new == nil) -> :sober
- new == nil -> nil
- alcohol_reached(old, new, 0.5) -> :stopconduire
- alcohol_reached(old, new, 1.0) -> :g1
- alcohol_reached(old, new, 2.0) -> :g2
- alcohol_reached(old, new, 3.0) -> :g3
- alcohol_reached(old, new, 4.0) -> :g4
- alcohol_reached(old, new, 5.0) -> :g5
- alcohol_reached(old, new, 6.0) -> :g6
- alcohol_reached(old, new, 7.0) -> :g7
- alcohol_reached(old, new, 10.0) -> :g10
- alcohol_reached(old, new, 13.74) -> :record
- alcohol_below(old, new, 0.5) -> :conduire
- alcohol_below(old, new, 1.0) -> :fini1g
- alcohol_below(old, new, 2.0) -> :fini2g
- alcohol_below(old, new, 3.0) -> :fini3g
- alcohol_below(old, new, 4.0) -> :fini4g
- (old.rising) && (!new.rising) -> :lowering
- true -> nil
+ {acct, event}
end
- {acct, event}
- end
for {acct, event} <- events do
- message = case event do
- :g1 -> [
- "[vigicuite jaune] LE GRAMME! LE GRAMME O/",
- "début de vigicuite jaune ! LE GRAMME ! \\O/",
- "waiiiiiiii le grammmeee",
- "bourraiiiiiiiiiiide 1 grammeeeeeeeeeee",
- ]
- :g2 -> [
- "[vigicuite orange] \\o_YAY 2 GRAMMES ! _o/",
- "PAITAIIIIIIIIII DEUX GRAMMEESSSSSSSSSSSSSSSSS",
- "bourrrrrraiiiiiiiiiiiiiiiide 2 grammeeeeeeeeeees",
- ]
- :g3 -> [
- "et un ! et deux ! et TROIS GRAMMEEESSSSSSS",
- "[vigicuite rouge] _o/ BOURRAIIDDDEEEE 3 GRAMMESSSSSSSSS \\o/ \\o/"
- ]
- :g4 -> [
- "[vigicuite écarlate] et un, et deux, et trois, ET QUATRES GRAMMEESSSSSSSSSSSSSSSSSSSssssss"
- ]
- :g5 -> "[vigicuite écarlate+] PUTAIN 5 GRAMMES !"
- :g6 -> "[vigicuite écarlate++] 6 grammes ? Vous pouvez joindre Alcool info service au 0 980 980 930"
- :g7 -> "[vigicuite c'est la merde] 7 grammes. Le SAMU, c'est le 15."
- :g10 -> "BORDLE 10 GRAMMES"
- :record -> "RECORD DU MONDE BATTU ! >13.74g/l !!"
- :fini1g -> [
- "fin d'alerte vigicuite jaune, passage en vert (<1g/l)",
- "/!\\ alerte moins de 1g/l /!\\"
- ]
- :fini2g -> [
- "t'as moins de 2 g/l, faut se reprendre là [vigicuite jaune]"
- ]
- :fini3g -> [
- "fin d'alerte vigicuite rouge, passage en orange (<3g/l)"
- ]
- :fini4g -> [
- "fin d'alerte vigicuite écarlate, passage en rouge (<4g/l)"
- ]
- :lowering -> [
- "attention ça baisse!",
- "tu vas quand même pas en rester là ?",
- "IL FAUT CONTINUER À BOIRE !",
- "t'abandonnes déjà ?",
- "!santai ?",
- "faut pas en rester là",
- "il faut se resservir",
- "coucou faut reboire",
- "encore un petit verre ?",
- "abwaaaaaaaaaaaaarrrrrrrrrrrrrr",
- "taux d'alcoolémie en chute ! agissez avant qu'il soit trop tard!",
- "ÇA BAISSE !!"
- ]
- :stopconduire -> [
- "0.5g! bientot le gramme?",
- "tu peux plus prendre la route... mais... tu peux prendre la route du gramme! !santai !",
- "fini la conduite!",
- "0.5! continues faut pas en rester là!",
- "beau début, continues !",
- "ça monte! 0.5g/l!"
- ]
- :conduire -> [
- "tu peux conduire, ou recommencer à boire! niveau critique!",
- "!santai ?",
- "tu peux reprendre la route, ou reprendre la route du gramme..",
- "attention, niveau critique!",
- "il faut boire !!",
- "trop de sang dans ton alcool, c'est mauvais pour la santé",
- "faut pas en rester là !",
- ]
- :sober -> [
- "sobre…",
- "/!\\ alerte sobriété /!\\",
- "... sobre?!?!",
- "sobre :(",
- "attention, t'es sobre :/",
- "danger, alcoolémie à 0.0 !",
- "sobre! c'était bien on recommence quand ?",
- "sobre ? Faut recommencer...",
- "T'es sobre. Ne te laisses pas abattre- ton caviste peut aider.",
- "Vous êtes sobre ? Ceci n'est pas une fatalité - resservez vous vite !"
- ]
- _ -> nil
- end
- message = case message do
- m when is_binary(m) -> m
- m when is_list(m) -> m |> Enum.shuffle() |> Enum.random()
- nil -> nil
- end
+ message =
+ case event do
+ :g1 ->
+ [
+ "[vigicuite jaune] LE GRAMME! LE GRAMME O/",
+ "début de vigicuite jaune ! LE GRAMME ! \\O/",
+ "waiiiiiiii le grammmeee",
+ "bourraiiiiiiiiiiide 1 grammeeeeeeeeeee"
+ ]
+
+ :g2 ->
+ [
+ "[vigicuite orange] \\o_YAY 2 GRAMMES ! _o/",
+ "PAITAIIIIIIIIII DEUX GRAMMEESSSSSSSSSSSSSSSSS",
+ "bourrrrrraiiiiiiiiiiiiiiiide 2 grammeeeeeeeeeees"
+ ]
+
+ :g3 ->
+ [
+ "et un ! et deux ! et TROIS GRAMMEEESSSSSSS",
+ "[vigicuite rouge] _o/ BOURRAIIDDDEEEE 3 GRAMMESSSSSSSSS \\o/ \\o/"
+ ]
+
+ :g4 ->
+ [
+ "[vigicuite écarlate] et un, et deux, et trois, ET QUATRES GRAMMEESSSSSSSSSSSSSSSSSSSssssss"
+ ]
+
+ :g5 ->
+ "[vigicuite écarlate+] PUTAIN 5 GRAMMES !"
+
+ :g6 ->
+ "[vigicuite écarlate++] 6 grammes ? Vous pouvez joindre Alcool info service au 0 980 980 930"
+
+ :g7 ->
+ "[vigicuite c'est la merde] 7 grammes. Le SAMU, c'est le 15."
+
+ :g10 ->
+ "BORDLE 10 GRAMMES"
+
+ :record ->
+ "RECORD DU MONDE BATTU ! >13.74g/l !!"
+
+ :fini1g ->
+ [
+ "fin d'alerte vigicuite jaune, passage en vert (<1g/l)",
+ "/!\\ alerte moins de 1g/l /!\\"
+ ]
+
+ :fini2g ->
+ [
+ "t'as moins de 2 g/l, faut se reprendre là [vigicuite jaune]"
+ ]
+
+ :fini3g ->
+ [
+ "fin d'alerte vigicuite rouge, passage en orange (<3g/l)"
+ ]
+
+ :fini4g ->
+ [
+ "fin d'alerte vigicuite écarlate, passage en rouge (<4g/l)"
+ ]
+
+ :lowering ->
+ [
+ "attention ça baisse!",
+ "tu vas quand même pas en rester là ?",
+ "IL FAUT CONTINUER À BOIRE !",
+ "t'abandonnes déjà ?",
+ "!santai ?",
+ "faut pas en rester là",
+ "il faut se resservir",
+ "coucou faut reboire",
+ "encore un petit verre ?",
+ "abwaaaaaaaaaaaaarrrrrrrrrrrrrr",
+ "taux d'alcoolémie en chute ! agissez avant qu'il soit trop tard!",
+ "ÇA BAISSE !!"
+ ]
+
+ :stopconduire ->
+ [
+ "0.5g! bientot le gramme?",
+ "tu peux plus prendre la route... mais... tu peux prendre la route du gramme! !santai !",
+ "fini la conduite!",
+ "0.5! continues faut pas en rester là!",
+ "beau début, continues !",
+ "ça monte! 0.5g/l!"
+ ]
+
+ :conduire ->
+ [
+ "tu peux conduire, ou recommencer à boire! niveau critique!",
+ "!santai ?",
+ "tu peux reprendre la route, ou reprendre la route du gramme..",
+ "attention, niveau critique!",
+ "il faut boire !!",
+ "trop de sang dans ton alcool, c'est mauvais pour la santé",
+ "faut pas en rester là !"
+ ]
+
+ :sober ->
+ [
+ "sobre…",
+ "/!\\ alerte sobriété /!\\",
+ "... sobre?!?!",
+ "sobre :(",
+ "attention, t'es sobre :/",
+ "danger, alcoolémie à 0.0 !",
+ "sobre! c'était bien on recommence quand ?",
+ "sobre ? Faut recommencer...",
+ "T'es sobre. Ne te laisses pas abattre- ton caviste peut aider.",
+ "Vous êtes sobre ? Ceci n'est pas une fatalité - resservez vous vite !"
+ ]
+
+ _ ->
+ nil
+ end
+
+ message =
+ case message do
+ m when is_binary(m) -> m
+ m when is_list(m) -> m |> Enum.shuffle() |> Enum.random()
+ nil -> nil
+ end
+
if message do
- #IO.puts("#{acct}: #{message}")
+ # IO.puts("#{acct}: #{message}")
account = Nola.Account.get(acct)
+
for {net, chan} <- Nola.Membership.notify_channels(account) do
user = Nola.UserTrack.find_by_account(net, account)
nick = if(user, do: user.nick, else: account.name)
@@ -245,8 +296,8 @@ defmodule Nola.Plugins.AlcoologAnnouncer do
timer()
- #IO.puts "tick stats ok"
- {:noreply, {stats,now,dets,ets}}
+ # IO.puts "tick stats ok"
+ {:noreply, {stats, now, dets, ets}}
end
def handle_info(_, state) do
@@ -265,5 +316,4 @@ defmodule Nola.Plugins.AlcoologAnnouncer do
defp timer() do
Process.send_after(self(), :stats, :timer.seconds(@seconds))
end
-
end
diff --git a/lib/plugins/base.ex b/lib/plugins/base.ex
index 97aaa05..7fb285b 100644
--- a/lib/plugins/base.ex
+++ b/lib/plugins/base.ex
@@ -1,5 +1,4 @@
defmodule Nola.Plugins.Base do
-
def irc_doc, do: nil
def start_link() do
@@ -17,85 +16,96 @@ defmodule Nola.Plugins.Base do
end
def handle_info({:irc, :trigger, "plugins", msg = %{trigger: %{type: :bang, args: []}}}, _) do
- enabled_string = Nola.Plugins.enabled()
- |> Enum.map(fn(string_or_module) ->
- case string_or_module do
- string when is_binary(string) -> string
- module when is_atom(module) ->
- module
- |> Macro.underscore()
- |> String.split("/", parts: :infinity)
- |> List.last()
+ enabled_string =
+ Nola.Plugins.enabled()
+ |> Enum.map(fn string_or_module ->
+ case string_or_module do
+ string when is_binary(string) ->
+ string
+
+ module when is_atom(module) ->
+ module
+ |> Macro.underscore()
+ |> String.split("/", parts: :infinity)
+ |> List.last()
end
- end)
- |> Enum.sort()
- |> Enum.join(", ")
+ end)
+ |> Enum.sort()
+ |> Enum.join(", ")
+
msg.replyfun.("Enabled plugins: #{enabled_string}")
{:noreply, nil}
end
def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :query, args: [plugin]}} = m}, _) do
module = Module.concat([Nola.Plugins, Macro.camelize(plugin)])
+
with true <- Code.ensure_loaded?(module),
- pid when is_pid(pid) <- GenServer.whereis(module)
- do
+ pid when is_pid(pid) <- GenServer.whereis(module) do
m.replyfun.("loaded, active: #{inspect(pid)}")
else
- false -> m.replyfun.("not loaded")
+ false ->
+ m.replyfun.("not loaded")
+
nil ->
- msg = case Nola.Plugins.get(module) do
- :disabled -> "disabled"
- {_, false, _} -> "disabled"
- _ -> "not active"
- end
+ msg =
+ case Nola.Plugins.get(module) do
+ :disabled -> "disabled"
+ {_, false, _} -> "disabled"
+ _ -> "not active"
+ end
+
m.replyfun.(msg)
end
+
{:noreply, nil}
end
def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :plus, args: [plugin]}} = m}, _) do
module = Module.concat([Nola.Plugins, Macro.camelize(plugin)])
+
with true <- Code.ensure_loaded?(module),
Nola.Plugins.switch(module, true),
- {:ok, pid} <- Nola.Plugins.start(module)
- do
+ {:ok, pid} <- Nola.Plugins.start(module) do
m.replyfun.("started: #{inspect(pid)}")
else
false -> m.replyfun.("not loaded")
:ignore -> m.replyfun.("disabled or throttled")
{:error, _} -> m.replyfun.("start error")
end
+
{:noreply, nil}
end
def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :tilde, args: [plugin]}} = m}, _) do
module = Module.concat([Nola.Plugins, Macro.camelize(plugin)])
+
with true <- Code.ensure_loaded?(module),
pid when is_pid(pid) <- GenServer.whereis(module),
:ok <- GenServer.stop(pid),
- {:ok, pid} <- Nola.Plugins.start(module)
- do
+ {:ok, pid} <- Nola.Plugins.start(module) do
m.replyfun.("restarted: #{inspect(pid)}")
else
false -> m.replyfun.("not loaded")
nil -> m.replyfun.("not active")
end
+
{:noreply, nil}
end
-
def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :minus, args: [plugin]}} = m}, _) do
module = Module.concat([Nola.Plugins, Macro.camelize(plugin)])
+
with true <- Code.ensure_loaded?(module),
pid when is_pid(pid) <- GenServer.whereis(module),
- :ok <- GenServer.stop(pid)
- do
+ :ok <- GenServer.stop(pid) do
Nola.Plugins.switch(module, false)
m.replyfun.("stopped: #{inspect(pid)}")
else
false -> m.replyfun.("not loaded")
nil -> m.replyfun.("not active")
end
+
{:noreply, nil}
end
@@ -106,7 +116,14 @@ defmodule Nola.Plugins.Base do
end
def handle_info({:irc, :trigger, "help", m = %{trigger: %{type: :bang}}}, _) do
- url = NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :index, m.network, NolaWeb.format_chan(m.channel))
+ url =
+ NolaWeb.Router.Helpers.irc_url(
+ NolaWeb.Endpoint,
+ :index,
+ m.network,
+ NolaWeb.format_chan(m.channel)
+ )
+
m.replyfun.("-> #{url}")
{:noreply, nil}
end
@@ -115,7 +132,10 @@ defmodule Nola.Plugins.Base do
{:ok, vsn} = :application.get_key(:nola, :vsn)
ver = List.to_string(vsn)
url = NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :index)
- elixir_ver = Application.started_applications() |> List.keyfind(:elixir, 0) |> elem(2) |> to_string()
+
+ elixir_ver =
+ Application.started_applications() |> List.keyfind(:elixir, 0) |> elem(2) |> to_string()
+
otp_ver = :erlang.system_info(:system_version) |> to_string() |> String.trim()
system = :erlang.system_info(:system_architecture) |> to_string()
brand = Nola.brand(:name)
@@ -123,22 +143,24 @@ defmodule Nola.Plugins.Base do
if message.channel do
message.replyfun.([
- <<"🤖 ", 2, "#{brand}", 2, " v", 2, "#{ver}", 2, "! My owner is #{Nola.brand(:owner)} and help is at #{url}">>,
+ <<"🤖 ", 2, "#{brand}", 2, " v", 2, "#{ver}", 2,
+ "! My owner is #{Nola.brand(:owner)} and help is at #{url}">>
])
else
message.replyfun.([
- <<"🤖 I am a robot running ", 2, "#{brand}", 2, " version ", 2, "#{ver}", 2, " — source: #{Nola.source_url()}">>,
+ <<"🤖 I am a robot running ", 2, "#{brand}", 2, " version ", 2, "#{ver}", 2,
+ " — source: #{Nola.source_url()}">>,
"Source code: #{Nola.source_url()}",
"🦾 Elixir #{elixir_ver} #{otp_ver} on #{system}",
"🙋🏻 Owner: #{owner}",
- "🌍 Web interface: #{url}"
+ "🌍 Web interface: #{url}"
])
end
+
{:noreply, nil}
end
def handle_info(_msg, _) do
{:noreply, nil}
end
-
end
diff --git a/lib/plugins/buffer.ex b/lib/plugins/buffer.ex
index 42a435e..5f848ef 100644
--- a/lib/plugins/buffer.ex
+++ b/lib/plugins/buffer.ex
@@ -6,7 +6,12 @@ defmodule Nola.Plugins.Buffer do
def select_buffer(network, channel, limit \\ 50) do
import Ex2ms
- spec = fun do {{n, c, _}, m} when n == ^network and (c == ^channel or is_nil(c)) -> m end
+
+ spec =
+ fun do
+ {{n, c, _}, m} when n == ^network and (c == ^channel or is_nil(c)) -> m
+ end
+
:ets.select(@table, spec, limit)
end
@@ -18,6 +23,7 @@ defmodule Nola.Plugins.Buffer do
for e <- ~w(messages triggers events outputs) do
{:ok, _} = Registry.register(Nola.PubSub, e, plugin: __MODULE__)
end
+
{:ok, :ets.new(@table, [:named_table, :ordered_set, :protected])}
end
@@ -34,11 +40,11 @@ defmodule Nola.Plugins.Buffer do
defp ts(nil), do: ts(NaiveDateTime.utc_now())
defp ts(naive = %NaiveDateTime{}) do
- ts = naive
- |> DateTime.from_naive!("Etc/UTC")
- |> DateTime.to_unix()
+ ts =
+ naive
+ |> DateTime.from_naive!("Etc/UTC")
+ |> DateTime.to_unix()
-ts
end
-
end
diff --git a/lib/plugins/calc.ex b/lib/plugins/calc.ex
index 2ff6cb4..20442ce 100644
--- a/lib/plugins/calc.ex
+++ b/lib/plugins/calc.ex
@@ -12,20 +12,27 @@ defmodule Nola.Plugins.Calc do
end
def init(_) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:calc", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:calc", plugin: __MODULE__)
{:ok, nil}
end
- def handle_info({:irc, :trigger, "calc", message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: expr_list}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "calc",
+ message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: expr_list}}},
+ state
+ ) do
expr = Enum.join(expr_list, " ")
- result = try do
- case Abacus.eval(expr) do
- {:ok, result} -> result
- error -> inspect(error)
+
+ result =
+ try do
+ case Abacus.eval(expr) do
+ {:ok, result} -> result
+ error -> inspect(error)
+ end
+ rescue
+ error -> if(error[:message], do: "#{error.message}", else: "erreur")
end
- rescue
- error -> if(error[:message], do: "#{error.message}", else: "erreur")
- end
+
message.replyfun.("#{message.sender.nick}: #{expr} = #{result}")
{:noreply, state}
end
@@ -33,5 +40,4 @@ defmodule Nola.Plugins.Calc do
def handle_info(msg, state) do
{:noreply, state}
end
-
end
diff --git a/lib/plugins/coronavirus.ex b/lib/plugins/coronavirus.ex
index afd8a33..db9f646 100644
--- a/lib/plugins/coronavirus.ex
+++ b/lib/plugins/coronavirus.ex
@@ -1,6 +1,7 @@
defmodule Nola.Plugins.Coronavirus do
require Logger
NimbleCSV.define(CovidCsv, separator: ",", escape: "\"")
+
@moduledoc """
# Corona Virus
@@ -19,7 +20,7 @@ defmodule Nola.Plugins.Coronavirus do
end
def init(_) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:coronavirus", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:coronavirus", plugin: __MODULE__)
{:ok, nil, {:continue, :init}}
:ignore
end
@@ -38,53 +39,87 @@ defmodule Nola.Plugins.Coronavirus do
{:noreply, %{data: data}}
end
- def handle_info({:irc, :trigger, "coronavirus", m = %Nola.Message{trigger: %{type: :bang, args: args}}}, state) when args in [
- [], ["morts"], ["confirmés"], ["soignés"], ["malades"], ["n"], ["nmorts"], ["nsoignés"], ["nconfirmés"]] do
- {field, name} = case args do
- ["confirmés"] -> {:confirmed, "confirmés"}
- ["morts"] -> {:deaths, "morts"}
- ["soignés"] -> {:recovered, "soignés"}
- ["nmorts"] -> {:new_deaths, "nouveaux morts"}
- ["nconfirmés"] -> {:new_confirmed, "nouveaux confirmés"}
- ["n"] -> {:new_current, "nouveaux malades"}
- ["nsoignés"] -> {:new_recovered, "nouveaux soignés"}
- _ -> {:current, "malades"}
- end
- IO.puts("FIELD #{inspect field}")
+ def handle_info(
+ {:irc, :trigger, "coronavirus", m = %Nola.Message{trigger: %{type: :bang, args: args}}},
+ state
+ )
+ when args in [
+ [],
+ ["morts"],
+ ["confirmés"],
+ ["soignés"],
+ ["malades"],
+ ["n"],
+ ["nmorts"],
+ ["nsoignés"],
+ ["nconfirmés"]
+ ] do
+ {field, name} =
+ case args do
+ ["confirmés"] -> {:confirmed, "confirmés"}
+ ["morts"] -> {:deaths, "morts"}
+ ["soignés"] -> {:recovered, "soignés"}
+ ["nmorts"] -> {:new_deaths, "nouveaux morts"}
+ ["nconfirmés"] -> {:new_confirmed, "nouveaux confirmés"}
+ ["n"] -> {:new_current, "nouveaux malades"}
+ ["nsoignés"] -> {:new_recovered, "nouveaux soignés"}
+ _ -> {:current, "malades"}
+ end
+
+ IO.puts("FIELD #{inspect(field)}")
field_evol = String.to_atom("new_#{field}")
- sorted = state.data
- |> Enum.filter(fn({_, %{region: region}}) -> region == true end)
- |> Enum.map(fn({location, data}) -> {location, Map.get(data, field, 0), Map.get(data, field_evol, 0)} end)
- |> Enum.sort_by(fn({_,count,_}) -> count end, &>=/2)
- |> Enum.take(10)
- |> Enum.with_index()
- |> Enum.map(fn({{location, count, evol}, index}) ->
- ev = if String.starts_with?(name, "nouveaux") do
- ""
- else
- " (#{Util.plusminus(evol)})"
- end
- "##{index+1}: #{location} #{count}#{ev}"
- end)
- |> Enum.intersperse(" - ")
- |> Enum.join()
+
+ sorted =
+ state.data
+ |> Enum.filter(fn {_, %{region: region}} -> region == true end)
+ |> Enum.map(fn {location, data} ->
+ {location, Map.get(data, field, 0), Map.get(data, field_evol, 0)}
+ end)
+ |> Enum.sort_by(fn {_, count, _} -> count end, &>=/2)
+ |> Enum.take(10)
+ |> Enum.with_index()
+ |> Enum.map(fn {{location, count, evol}, index} ->
+ ev =
+ if String.starts_with?(name, "nouveaux") do
+ ""
+ else
+ " (#{Util.plusminus(evol)})"
+ end
+
+ "##{index + 1}: #{location} #{count}#{ev}"
+ end)
+ |> Enum.intersperse(" - ")
+ |> Enum.join()
+
m.replyfun.("CORONAVIRUS TOP10 #{name}: " <> sorted)
{:noreply, state}
end
- def handle_info({:irc, :trigger, "coronavirus", m = %Nola.Message{trigger: %{type: :bang, args: location}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "coronavirus",
+ m = %Nola.Message{trigger: %{type: :bang, args: location}}},
+ state
+ ) do
location = Enum.join(location, " ") |> String.downcase()
+
if data = Map.get(state.data, location) do
- m.replyfun.("coronavirus: #{location}: "
- <> "#{data.current} malades (#{Util.plusminus(data.new_current)}), "
- <> "#{data.confirmed} confirmés (#{Util.plusminus(data.new_confirmed)}), "
- <> "#{data.deaths} morts (#{Util.plusminus(data.new_deaths)}), "
- <> "#{data.recovered} soignés (#{Util.plusminus(data.new_recovered)}) (@ #{data.update})")
+ m.replyfun.(
+ "coronavirus: #{location}: " <>
+ "#{data.current} malades (#{Util.plusminus(data.new_current)}), " <>
+ "#{data.confirmed} confirmés (#{Util.plusminus(data.new_confirmed)}), " <>
+ "#{data.deaths} morts (#{Util.plusminus(data.new_deaths)}), " <>
+ "#{data.recovered} soignés (#{Util.plusminus(data.new_recovered)}) (@ #{data.update})"
+ )
end
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "coronavirus", m = %Nola.Message{trigger: %{type: :query, args: location}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "coronavirus",
+ m = %Nola.Message{trigger: %{type: :query, args: location}}},
+ state
+ ) do
m.replyfun.("https://github.com/CSSEGISandData/COVID-19")
{:noreply, state}
end
@@ -93,80 +128,125 @@ defmodule Nola.Plugins.Coronavirus do
# 2. Fetch yesterday if no results
defp fetch_data(current_data, date \\ nil) do
now = Date.utc_today()
- url = fn(date) ->
+
+ url = fn date ->
"https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_daily_reports/#{date}.csv"
end
+
request_date = date || now
- Logger.debug("Coronavirus check date: #{inspect request_date}")
- {:ok, date_s} = Timex.format({request_date.year, request_date.month, request_date.day}, "%m-%d-%Y", :strftime)
+ Logger.debug("Coronavirus check date: #{inspect(request_date)}")
+
+ {:ok, date_s} =
+ Timex.format(
+ {request_date.year, request_date.month, request_date.day},
+ "%m-%d-%Y",
+ :strftime
+ )
+
cur_url = url.(date_s)
- Logger.debug "Fetching URL #{cur_url}"
+ Logger.debug("Fetching URL #{cur_url}")
+
case HTTPoison.get(cur_url, [], follow_redirect: true) do
{:ok, %HTTPoison.Response{status_code: 200, body: csv}} ->
# Parse CSV update data
- data = csv
- |> CovidCsv.parse_string()
- |> Enum.reduce(%{}, fn(line, acc) ->
- case line do
- # FIPS,Admin2,Province_State,Country_Region,Last_Update,Lat,Long_,Confirmed,Deaths,Recovered,Active,Combined_Key
- #0FIPS,Admin2,Province_State,Country_Region,Last_Update,Lat,Long_,Confirmed,Deaths,Recovered,Active,Combined_Key,Incidence_Rate,Case-Fatality_Ratio
- [_, _, state, region, update, _lat, _lng, confirmed, deaths, recovered, _active, _combined_key, _incidence_rate, _fatality_ratio] ->
- state = String.downcase(state)
- region = String.downcase(region)
- confirmed = String.to_integer(confirmed)
- deaths = String.to_integer(deaths)
- recovered = String.to_integer(recovered)
-
- current = (confirmed - recovered) - deaths
-
- entry = %{update: update, confirmed: confirmed, deaths: deaths, recovered: recovered, current: current, region: region}
-
- region_entry = Map.get(acc, region, %{update: nil, confirmed: 0, deaths: 0, recovered: 0, current: 0})
- region_entry = %{
- update: region_entry.update || update,
- confirmed: region_entry.confirmed + confirmed,
- deaths: region_entry.deaths + deaths,
- current: region_entry.current + current,
- recovered: region_entry.recovered + recovered,
- region: true
- }
-
- changes = if old = Map.get(current_data, region) do
- %{
- new_confirmed: region_entry.confirmed - old.confirmed,
- new_current: region_entry.current - old.current,
- new_deaths: region_entry.deaths - old.deaths,
- new_recovered: region_entry.recovered - old.recovered,
+ data =
+ csv
+ |> CovidCsv.parse_string()
+ |> Enum.reduce(%{}, fn line, acc ->
+ case line do
+ # FIPS,Admin2,Province_State,Country_Region,Last_Update,Lat,Long_,Confirmed,Deaths,Recovered,Active,Combined_Key
+ # 0FIPS,Admin2,Province_State,Country_Region,Last_Update,Lat,Long_,Confirmed,Deaths,Recovered,Active,Combined_Key,Incidence_Rate,Case-Fatality_Ratio
+ [
+ _,
+ _,
+ state,
+ region,
+ update,
+ _lat,
+ _lng,
+ confirmed,
+ deaths,
+ recovered,
+ _active,
+ _combined_key,
+ _incidence_rate,
+ _fatality_ratio
+ ] ->
+ state = String.downcase(state)
+ region = String.downcase(region)
+ confirmed = String.to_integer(confirmed)
+ deaths = String.to_integer(deaths)
+ recovered = String.to_integer(recovered)
+
+ current = confirmed - recovered - deaths
+
+ entry = %{
+ update: update,
+ confirmed: confirmed,
+ deaths: deaths,
+ recovered: recovered,
+ current: current,
+ region: region
}
- else
- %{new_confirmed: 0, new_current: 0, new_deaths: 0, new_recovered: 0}
- end
- region_entry = Map.merge(region_entry, changes)
-
- acc = Map.put(acc, region, region_entry)
+ region_entry =
+ Map.get(acc, region, %{
+ update: nil,
+ confirmed: 0,
+ deaths: 0,
+ recovered: 0,
+ current: 0
+ })
+
+ region_entry = %{
+ update: region_entry.update || update,
+ confirmed: region_entry.confirmed + confirmed,
+ deaths: region_entry.deaths + deaths,
+ current: region_entry.current + current,
+ recovered: region_entry.recovered + recovered,
+ region: true
+ }
- acc = if state && state != "" do
- Map.put(acc, state, entry)
- else
+ changes =
+ if old = Map.get(current_data, region) do
+ %{
+ new_confirmed: region_entry.confirmed - old.confirmed,
+ new_current: region_entry.current - old.current,
+ new_deaths: region_entry.deaths - old.deaths,
+ new_recovered: region_entry.recovered - old.recovered
+ }
+ else
+ %{new_confirmed: 0, new_current: 0, new_deaths: 0, new_recovered: 0}
+ end
+
+ region_entry = Map.merge(region_entry, changes)
+
+ acc = Map.put(acc, region, region_entry)
+
+ acc =
+ if state && state != "" do
+ Map.put(acc, state, entry)
+ else
+ acc
+ end
+
+ other ->
+ Logger.info("Coronavirus line failed: #{inspect(line)}")
acc
- end
+ end
+ end)
- other ->
- Logger.info("Coronavirus line failed: #{inspect line}")
- acc
- end
- end)
- Logger.info "Updated coronavirus database"
+ Logger.info("Updated coronavirus database")
{data, :timer.minutes(60)}
+
{:ok, %HTTPoison.Response{status_code: 404}} ->
- Logger.debug "Corona 404 #{cur_url}"
+ Logger.debug("Corona 404 #{cur_url}")
date = Date.add(date || now, -1)
fetch_data(current_data, date)
+
other ->
- Logger.error "Coronavirus: Update failed #{inspect other}"
+ Logger.error("Coronavirus: Update failed #{inspect(other)}")
{current_data, :timer.minutes(5)}
end
end
-
end
diff --git a/lib/plugins/correction.ex b/lib/plugins/correction.ex
index b50733b..0d3f5bd 100644
--- a/lib/plugins/correction.ex
+++ b/lib/plugins/correction.ex
@@ -6,13 +6,14 @@ defmodule Nola.Plugins.Correction do
"""
def irc_doc, do: @moduledoc
+
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
- {:ok, _} = Registry.register(Nola.PubSub, "messages", [plugin: __MODULE__])
- {:ok, _} = Registry.register(Nola.PubSub, "triggers", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "messages", plugin: __MODULE__)
+ {:ok, _} = Registry.register(Nola.PubSub, "triggers", plugin: __MODULE__)
{:ok, %{}}
end
@@ -27,33 +28,40 @@ defmodule Nola.Plugins.Correction 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
["s", match, replace | _] ->
case Regex.compile(match) do
{:ok, reg} ->
- repl = Enum.find(history, fn(m) -> Regex.match?(reg, m.text) end)
+ repl = Enum.find(history, fn m -> Regex.match?(reg, m.text) end)
+
if repl do
new_text = String.replace(repl.text, reg, replace)
m.replyfun.("correction: <#{repl.sender.nick}> #{new_text}")
end
+
_ ->
m.replyfun.("correction: invalid regex")
end
- _ -> m.replyfun.("correction: invalid regex format")
+
+ _ ->
+ m.replyfun.("correction: invalid regex format")
end
+
state
else
- history = if length(history) > 100 do
- {_, history} = List.pop_at(history, 99)
- [m | history]
- else
- [m | history]
- end
+ history =
+ if length(history) > 100 do
+ {_, history} = List.pop_at(history, 99)
+ [m | history]
+ else
+ [m | history]
+ end
+
Map.put(state, key(m), history)
end
end
defp key(%{network: net, channel: chan}), do: "#{net}/#{chan}"
-
end
diff --git a/lib/plugins/dice.ex b/lib/plugins/dice.ex
index dcf7d0b..be0d6f4 100644
--- a/lib/plugins/dice.ex
+++ b/lib/plugins/dice.ex
@@ -21,23 +21,24 @@ defmodule Nola.Plugins.Dice do
end
def init([]) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:dice", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:dice", plugin: __MODULE__)
{:ok, %__MODULE__{}}
end
def handle_info({:irc, :trigger, _, message = %{trigger: %{type: :bang, args: args}}}, state) do
- to_integer = fn(string, default) ->
+ to_integer = fn string, default ->
case Integer.parse(string) do
{int, _} -> int
_ -> default
end
end
- {rolls, faces} = case args do
- [] -> {@default_rolls, @default_faces}
- [faces, rolls] -> {to_integer.(rolls, @default_rolls), to_integer.(faces, @default_faces)}
- [rolls] -> {to_integer.(rolls, @default_rolls), @default_faces}
- end
+ {rolls, faces} =
+ case args do
+ [] -> {@default_rolls, @default_faces}
+ [faces, rolls] -> {to_integer.(rolls, @default_rolls), to_integer.(faces, @default_faces)}
+ [rolls] -> {to_integer.(rolls, @default_rolls), @default_faces}
+ end
roll(state, message, faces, rolls)
@@ -49,18 +50,20 @@ defmodule Nola.Plugins.Dice do
end
defp roll(state, message, faces, 1) when faces > 0 do
- random = :crypto.rand_uniform(1, faces+1)
+ random = :crypto.rand_uniform(1, faces + 1)
message.replyfun.("#{message.sender.nick} dice: #{random}")
end
+
defp roll(state, message, faces, rolls) when faces > 0 and rolls > 0 and rolls <= @max_rolls do
- {results, acc} = Enum.map_reduce(Range.new(1, rolls), 0, fn(i, acc) ->
- random = :crypto.rand_uniform(1, faces+1)
- {random, acc + random}
- end)
+ {results, acc} =
+ Enum.map_reduce(Range.new(1, rolls), 0, fn i, acc ->
+ random = :crypto.rand_uniform(1, faces + 1)
+ {random, acc + random}
+ end)
+
results = Enum.join(results, "; ")
message.replyfun.("#{message.sender.nick} dice: [#{acc}] #{results}")
end
defp roll(_, _, _, _, _), do: nil
-
end
diff --git a/lib/plugins/finance.ex b/lib/plugins/finance.ex
index b083df8..68afc48 100644
--- a/lib/plugins/finance.ex
+++ b/lib/plugins/finance.ex
@@ -27,25 +27,31 @@ defmodule Nola.Plugins.Finance do
@crypto_list "http://www.alphavantage.co/digital_currency_list/"
HTTPoison.start()
- load_currency = fn(url) ->
+
+ load_currency = fn url ->
resp = HTTPoison.get!(url)
+
resp.body
|> String.strip()
|> String.split("\n")
|> Enum.drop(1)
- |> Enum.map(fn(line) ->
- [symbol, name] = line
- |> String.strip()
- |> String.split(",", parts: 2)
+ |> Enum.map(fn line ->
+ [symbol, name] =
+ line
+ |> String.strip()
+ |> String.split(",", parts: 2)
+
{symbol, name}
end)
- |> Enum.into(Map.new)
+ |> Enum.into(Map.new())
end
+
fiat = load_currency.(@currency_list)
crypto = load_currency.(@crypto_list)
@currencies Map.merge(fiat, crypto)
def irc_doc, do: @moduledoc
+
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@@ -58,58 +64,77 @@ defmodule Nola.Plugins.Finance do
{:ok, nil}
end
-
- def handle_info({:irc, :trigger, "stocks", message = %{trigger: %{type: :query, args: args = search}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "stocks", message = %{trigger: %{type: :query, args: args = search}}},
+ state
+ ) do
search(search, message)
{:noreply, state}
end
defp search(search, message) do
search = Enum.join(search, "%20")
- url = "https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=#{search}&apikey=#{api_key()}"
+
+ url =
+ "https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=#{search}&apikey=#{api_key()}"
+
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: data}} ->
data = Poison.decode!(data)
IO.inspect(data)
+
if error = Map.get(data, "Error Message") do
- Logger.error("AlphaVantage API invalid request #{url} - #{inspect error}")
+ Logger.error("AlphaVantage API invalid request #{url} - #{inspect(error)}")
message.replyfun.("stocks: requête invalide")
else
- items = for item <- Map.get(data, "bestMatches") do
- symbol = Map.get(item, "1. symbol")
- name = Map.get(item, "2. name")
- type = Map.get(item, "3. type")
- region = Map.get(item, "4. region")
- currency = Map.get(item, "8. currency")
- "#{symbol}: #{name} (#{region}; #{currency}; #{type})"
- end
- |> Enum.join(", ")
- items = if items == "" do
- "no results!"
- else
- items
- end
+ items =
+ for item <- Map.get(data, "bestMatches") do
+ symbol = Map.get(item, "1. symbol")
+ name = Map.get(item, "2. name")
+ type = Map.get(item, "3. type")
+ region = Map.get(item, "4. region")
+ currency = Map.get(item, "8. currency")
+ "#{symbol}: #{name} (#{region}; #{currency}; #{type})"
+ end
+ |> Enum.join(", ")
+
+ items =
+ if items == "" do
+ "no results!"
+ else
+ items
+ end
+
message.replyfun.(items)
end
+
{:ok, resp = %HTTPoison.Response{status_code: code}} ->
- Logger.error "AlphaVantage API error: #{code} #{url} - #{inspect resp}"
+ Logger.error("AlphaVantage API error: #{code} #{url} - #{inspect(resp)}")
message.replyfun.("forex: erreur (api #{code})")
+
{:error, %HTTPoison.Error{reason: error}} ->
- Logger.error "AlphaVantage HTTP error: #{inspect error}"
- message.replyfun.("forex: erreur (http #{inspect error})")
+ Logger.error("AlphaVantage HTTP error: #{inspect(error)}")
+ message.replyfun.("forex: erreur (http #{inspect(error)})")
end
end
- def handle_info({:irc, :trigger, "stocks", message = %{trigger: %{type: :bang, args: args = [symbol]}}}, state) do
- url = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=#{symbol}&apikey=#{api_key()}"
+ def handle_info(
+ {:irc, :trigger, "stocks", message = %{trigger: %{type: :bang, args: args = [symbol]}}},
+ state
+ ) do
+ url =
+ "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=#{symbol}&apikey=#{api_key()}"
+
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: data}} ->
data = Poison.decode!(data)
IO.inspect(data)
+
case data do
%{"Error Message" => error} ->
- Logger.error("AlphaVantage API invalid request #{url} - #{inspect error}")
+ Logger.error("AlphaVantage API invalid request #{url} - #{inspect(error)}")
message.replyfun.("stocks: error: #{error}")
+
%{"Global Quote" => data = %{"01. symbol" => _}} ->
open = Map.get(data, "02. open")
high = Map.get(data, "03. high")
@@ -120,39 +145,54 @@ defmodule Nola.Plugins.Finance do
change = Map.get(data, "09. change")
change_pct = Map.get(data, "10. change percent")
- msg = "#{symbol}: #{price} #{change} [#{change_pct}] (high: #{high}, low: #{low}, open: #{open}, prev close: #{prev_close}) (volume: #{volume})"
+ msg =
+ "#{symbol}: #{price} #{change} [#{change_pct}] (high: #{high}, low: #{low}, open: #{open}, prev close: #{prev_close}) (volume: #{volume})"
+
message.replyfun.(msg)
+
_ ->
message.replyfun.("stocks: unknown symbol: #{symbol}")
search([symbol], message)
end
+
{:ok, resp = %HTTPoison.Response{status_code: code}} ->
- Logger.error "AlphaVantage API error: #{code} #{url} - #{inspect resp}"
+ Logger.error("AlphaVantage API error: #{code} #{url} - #{inspect(resp)}")
message.replyfun.("stocks: erreur (api #{code})")
+
{:error, %HTTPoison.Error{reason: error}} ->
- Logger.error "AlphaVantage HTTP error: #{inspect error}"
- message.replyfun.("stocks: erreur (http #{inspect error})")
+ Logger.error("AlphaVantage HTTP error: #{inspect(error)}")
+ message.replyfun.("stocks: erreur (http #{inspect(error)})")
end
+
{:noreply, state}
end
+ def handle_info(
+ {:irc, :trigger, "forex", message = %{trigger: %{type: :bang, args: args = [_ | _]}}},
+ state
+ ) do
+ {amount, from, to} =
+ case args do
+ [amount, from, to] ->
+ {amount, _} = Float.parse(amount)
+ {amount, from, to}
+
+ [from, to] ->
+ {1, from, to}
+
+ [from] ->
+ {1, from, "EUR"}
+ end
+
+ url =
+ "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=#{from}&to_currency=#{to}&apikey=#{api_key()}"
- def handle_info({:irc, :trigger, "forex", message = %{trigger: %{type: :bang, args: args = [_ | _]}}}, state) do
- {amount, from, to} = case args do
- [amount, from, to] ->
- {amount, _} = Float.parse(amount)
- {amount, from, to}
- [from, to] ->
- {1, from, to}
- [from] ->
- {1, from, "EUR"}
- end
- url = "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=#{from}&to_currency=#{to}&apikey=#{api_key()}"
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: data}} ->
data = Poison.decode!(data)
+
if error = Map.get(data, "Error Message") do
- Logger.error("AlphaVantage API invalid request #{url} - #{inspect error}")
+ Logger.error("AlphaVantage API invalid request #{url} - #{inspect(error)}")
message.replyfun.("forex: requête invalide")
else
data = Map.get(data, "Realtime Currency Exchange Rate")
@@ -160,34 +200,47 @@ defmodule Nola.Plugins.Finance do
to_name = Map.get(data, "4. To_Currency Name")
rate = Map.get(data, "5. Exchange Rate")
{rate, _} = Float.parse(rate)
- value = amount*rate
- message.replyfun.("#{amount} #{from} (#{from_name}) -> #{value} #{to} (#{to_name}) (#{rate})")
+ value = amount * rate
+
+ message.replyfun.(
+ "#{amount} #{from} (#{from_name}) -> #{value} #{to} (#{to_name}) (#{rate})"
+ )
end
+
{:ok, resp = %HTTPoison.Response{status_code: code}} ->
- Logger.error "AlphaVantage API error: #{code} #{url} - #{inspect resp}"
+ Logger.error("AlphaVantage API error: #{code} #{url} - #{inspect(resp)}")
message.replyfun.("forex: erreur (api #{code})")
+
{:error, %HTTPoison.Error{reason: error}} ->
- Logger.error "AlphaVantage HTTP error: #{inspect error}"
- message.replyfun.("forex: erreur (http #{inspect error})")
+ Logger.error("AlphaVantage HTTP error: #{inspect(error)}")
+ message.replyfun.("forex: erreur (http #{inspect(error)})")
end
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "currency", message = %{trigger: %{type: :query, args: args = search}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "currency", message = %{trigger: %{type: :query, args: args = search}}},
+ state
+ ) do
search = Enum.join(search, " ")
- results = Enum.filter(@currencies, fn({symbol, name}) ->
- String.contains?(String.downcase(name), String.downcase(search)) || String.contains?(String.downcase(symbol), String.downcase(search))
- end)
- |> Enum.map(fn({symbol, name}) ->
- "#{symbol}: #{name}"
- end)
- |> Enum.join(", ")
+
+ results =
+ Enum.filter(@currencies, fn {symbol, name} ->
+ String.contains?(String.downcase(name), String.downcase(search)) ||
+ String.contains?(String.downcase(symbol), String.downcase(search))
+ end)
+ |> Enum.map(fn {symbol, name} ->
+ "#{symbol}: #{name}"
+ end)
+ |> Enum.join(", ")
if results == "" do
message.replyfun.("no results!")
else
message.replyfun.(results)
end
+
{:noreply, state}
end
@@ -195,5 +248,4 @@ defmodule Nola.Plugins.Finance do
Application.get_env(:nola, :alphavantage, [])
|> Keyword.get(:api_key, "demo")
end
-
end
diff --git a/lib/plugins/helpers/temp_ref.ex b/lib/plugins/helpers/temp_ref.ex
index 160169d..f4407d8 100644
--- a/lib/plugins/helpers/temp_ref.ex
+++ b/lib/plugins/helpers/temp_ref.ex
@@ -30,7 +30,7 @@ defmodule Nola.Plugins.TempRefHelper do
length = Keyword.get(options, :length, 3)
for _ <- 1..length, into: "", do: <<Enum.random('bcdfghjkmpqtrvwxy2346789')>>
end
-
+
def increase(options) do
Keyword.put(options, :length, Keyword.get(options, :length, 3) + 1)
end
@@ -42,8 +42,13 @@ defmodule Nola.Plugins.TempRefHelper do
max: Keyword.get(options, :max, []),
expire: Keyword.get(options, :expire, :infinity),
build_fun: Keyword.get(options, :build_fun, &__MODULE__.SimpleAlphaNumericBuilder.build/1),
- build_increase_fun: Keyword.get(options, :build_increase_fun, &__MODULE__.SimpleAlphaNumericBuilder.increase/1),
- build_options: Keyword.get(options, :build_options, [length: 3])
+ build_increase_fun:
+ Keyword.get(
+ options,
+ :build_increase_fun,
+ &__MODULE__.SimpleAlphaNumericBuilder.increase/1
+ ),
+ build_options: Keyword.get(options, :build_options, length: 3)
}
end
@@ -58,6 +63,7 @@ defmodule Nola.Plugins.TempRefHelper do
def put_temp_ref(data, state = %__MODULE__{}) do
state = janitor_refs(state)
key = new_nonexisting_key(state)
+
if key do
ref = {key, DateTime.utc_now(), data}
{key, %__MODULE__{state | refs: [ref | state.refs]}}
@@ -78,18 +84,19 @@ defmodule Nola.Plugins.TempRefHelper do
end
defp new_nonexisting_key(state = %__MODULE__{refs: refs}, i \\ 1) do
- build_options = if rem(i, 5) == 0 do
- state.build_increase_fun.(state.build_options)
- else
- state.build_options
- end
-
+ build_options =
+ if rem(i, 5) == 0 do
+ state.build_increase_fun.(state.build_options)
+ else
+ state.build_options
+ end
+
key = state.build_fun.(state.build_options)
+
if !List.keymember?(refs, key, 0) do
key
else
new_nonexisting_key(state, i + 1)
end
end
-
end
diff --git a/lib/plugins/kick_roulette.ex b/lib/plugins/kick_roulette.ex
index 3f81977..2194775 100644
--- a/lib/plugins/kick_roulette.ex
+++ b/lib/plugins/kick_roulette.ex
@@ -6,27 +6,28 @@ defmodule Nola.Plugins.KickRoulette do
"""
def irc_doc, do: @moduledoc
+
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init([]) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:kick", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:kick", plugin: __MODULE__)
{:ok, nil}
end
def handle_info({:irc, :trigger, "kick", message = %{trigger: %{type: :bang, args: _}}}, _) do
if 5 == :crypto.rand_uniform(1, 6) do
- spawn(fn() ->
+ spawn(fn ->
:timer.sleep(:crypto.rand_uniform(200, 10_000))
message.replyfun.({:kick, message.sender.nick, "perdu"})
end)
end
+
{:noreply, nil}
end
def handle_info(msg, _) do
{:noreply, nil}
end
-
end
diff --git a/lib/plugins/last_fm.ex b/lib/plugins/last_fm.ex
index b7d0a92..0d513c4 100644
--- a/lib/plugins/last_fm.ex
+++ b/lib/plugins/last_fm.ex
@@ -23,34 +23,55 @@ defmodule Nola.Plugins.LastFm do
def init([]) do
regopts = [plugin: __MODULE__]
for t <- @pubsub_topics, do: {:ok, _} = Registry.register(Nola.PubSub, t, regopts)
- dets_filename = (Nola.data_path() <> "/" <> "lastfm.dets") |> String.to_charlist
+ dets_filename = (Nola.data_path() <> "/" <> "lastfm.dets") |> String.to_charlist()
{:ok, dets} = :dets.open_file(dets_filename, [])
{:ok, %__MODULE__{dets: dets}}
end
- def handle_info({:irc, :trigger, "lastfm", message = %{trigger: %{type: :plus, args: [username]}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "lastfm", message = %{trigger: %{type: :plus, args: [username]}}},
+ state
+ ) do
username = String.strip(username)
:ok = :dets.insert(state.dets, {message.account.id, username})
- message.replyfun.("#{message.sender.nick}: nom d'utilisateur last.fm configuré: \"#{username}\".")
+
+ message.replyfun.(
+ "#{message.sender.nick}: nom d'utilisateur last.fm configuré: \"#{username}\"."
+ )
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "lastfm", message = %{trigger: %{type: :minus, args: []}}}, state) do
- text = case :dets.lookup(state.dets, message.account.id) do
- [{_nick, _username}] ->
- :dets.delete(state.dets, message.account.id)
- message.replyfun.("#{message.sender.nick}: nom d'utilisateur last.fm enlevé.")
- _ -> nil
- end
+ def handle_info(
+ {:irc, :trigger, "lastfm", message = %{trigger: %{type: :minus, args: []}}},
+ state
+ ) do
+ text =
+ case :dets.lookup(state.dets, message.account.id) do
+ [{_nick, _username}] ->
+ :dets.delete(state.dets, message.account.id)
+ message.replyfun.("#{message.sender.nick}: nom d'utilisateur last.fm enlevé.")
+
+ _ ->
+ nil
+ end
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "lastfm", message = %{trigger: %{type: :query, args: []}}}, state) do
- text = case :dets.lookup(state.dets, message.account.id) do
- [{_nick, username}] ->
- message.replyfun.("#{message.sender.nick}: #{username}.")
- _ -> nil
- end
+ def handle_info(
+ {:irc, :trigger, "lastfm", message = %{trigger: %{type: :query, args: []}}},
+ state
+ ) do
+ text =
+ case :dets.lookup(state.dets, message.account.id) do
+ [{_nick, username}] ->
+ message.replyfun.("#{message.sender.nick}: #{username}.")
+
+ _ ->
+ nil
+ end
+
{:noreply, state}
end
@@ -59,18 +80,24 @@ defmodule Nola.Plugins.LastFm do
{:noreply, state}
end
- def handle_info({:irc, :trigger, _, message = %{trigger: %{type: :bang, args: [nick_or_user]}}}, state) do
+ def handle_info(
+ {:irc, :trigger, _, message = %{trigger: %{type: :bang, args: [nick_or_user]}}},
+ state
+ ) do
irc_now_playing(nick_or_user, message, state)
{:noreply, state}
end
def handle_info({:irc, :trigger, _, message = %{trigger: %{type: :dot}}}, state) do
members = Nola.Membership.members(message.network, message.channel)
- foldfun = fn({nick, user}, acc) -> [{nick,user}|acc] end
- usernames = :dets.foldl(foldfun, [], state.dets)
- |> Enum.uniq()
- |> Enum.filter(fn({acct,_}) -> Enum.member?(members, acct) end)
- |> Enum.map(fn({_, u}) -> u end)
+ foldfun = fn {nick, user}, acc -> [{nick, user} | acc] end
+
+ usernames =
+ :dets.foldl(foldfun, [], state.dets)
+ |> Enum.uniq()
+ |> Enum.filter(fn {acct, _} -> Enum.member?(members, acct) end)
+ |> Enum.map(fn {_, u} -> u end)
+
for u <- usernames, do: irc_now_playing(u, message, state)
{:noreply, state}
end
@@ -84,40 +111,50 @@ defmodule Nola.Plugins.LastFm do
:dets.sync(state.dets)
:dets.close(state.dets)
end
+
:ok
end
defp irc_now_playing(nick_or_user, message, state) do
nick_or_user = String.strip(nick_or_user)
- id_or_user = if account = Nola.Account.get(nick_or_user) || Nola.Account.find_always_by_nick(message.network, message.channel, nick_or_user) do
- account.id
- else
- nick_or_user
- end
-
- username = case :dets.lookup(state.dets, id_or_user) do
- [{_, username}] -> username
- _ -> id_or_user
- end
+ id_or_user =
+ if account =
+ Nola.Account.get(nick_or_user) ||
+ Nola.Account.find_always_by_nick(message.network, message.channel, nick_or_user) do
+ account.id
+ else
+ nick_or_user
+ end
+
+ username =
+ case :dets.lookup(state.dets, id_or_user) do
+ [{_, username}] -> username
+ _ -> id_or_user
+ end
case now_playing(username) do
{:error, text} when is_binary(text) ->
message.replyfun.(text)
+
{:ok, map} when is_map(map) ->
track = fetch_track(username, map)
text = format_now_playing(map, track)
- user = if account = Nola.Account.get(id_or_user) do
- user = Nola.UserTrack.find_by_account(message.network, account)
- if(user, do: user.nick, else: account.name)
- else
- username
- end
+
+ user =
+ if account = Nola.Account.get(id_or_user) do
+ user = Nola.UserTrack.find_by_account(message.network, account)
+ if(user, do: user.nick, else: account.name)
+ else
+ username
+ end
+
if user && text do
message.replyfun.("#{user} #{text}")
else
message.replyfun.("#{username}: pas de résultat")
end
+
other ->
message.replyfun.("erreur :(")
end
@@ -125,32 +162,57 @@ defmodule Nola.Plugins.LastFm do
defp now_playing(user) do
api = Application.get_env(:nola, :lastfm)[:api_key]
- url = "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&extended=1" <> "&api_key=" <> api <> "&user="<> user
+
+ url =
+ "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&extended=1" <>
+ "&api_key=" <> api <> "&user=" <> user
+
case HTTPoison.get(url) do
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> Jason.decode(body)
- {:ok, %HTTPoison.Response{status_code: 404}} -> {:error, "last.fm: utilisateur \"#{user}\" inexistant"}
- {:ok, %HTTPoison.Response{status_code: code}} -> {:error, "last.fm: erreur #{to_string(code)}"}
+ {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
+ Jason.decode(body)
+
+ {:ok, %HTTPoison.Response{status_code: 404}} ->
+ {:error, "last.fm: utilisateur \"#{user}\" inexistant"}
+
+ {:ok, %HTTPoison.Response{status_code: code}} ->
+ {:error, "last.fm: erreur #{to_string(code)}"}
+
error ->
- Logger.error "Lastfm http error: #{inspect error}"
+ Logger.error("Lastfm http error: #{inspect(error)}")
:error
end
end
- defp fetch_track(user, %{"recenttracks" => %{"track" => [ t = %{"name" => name, "artist" => %{"name" => artist}} | _]}}) do
+
+ defp fetch_track(user, %{
+ "recenttracks" => %{
+ "track" => [t = %{"name" => name, "artist" => %{"name" => artist}} | _]
+ }
+ }) do
api = Application.get_env(:nola, :lastfm)[:api_key]
- url = "http://ws.audioscrobbler.com/2.0/?method=track.getInfo&format=json" <> "&api_key=" <> api <> "&username="<> user <> "&artist="<>URI.encode(artist)<>"&track="<>URI.encode(name)
+
+ url =
+ "http://ws.audioscrobbler.com/2.0/?method=track.getInfo&format=json" <>
+ "&api_key=" <>
+ api <>
+ "&username=" <> user <> "&artist=" <> URI.encode(artist) <> "&track=" <> URI.encode(name)
+
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
case Jason.decode(body) do
{:ok, body} -> body["track"] || %{}
_ -> %{}
end
+
error ->
- Logger.error "Lastfm http error: #{inspect error}"
+ Logger.error("Lastfm http error: #{inspect(error)}")
:error
end
end
- defp format_now_playing(%{"recenttracks" => %{"track" => [track = %{"@attr" => %{"nowplaying" => "true"}} | _]}}, et) do
+ defp format_now_playing(
+ %{"recenttracks" => %{"track" => [track = %{"@attr" => %{"nowplaying" => "true"}} | _]}},
+ et
+ ) do
format_track(true, track, et)
end
@@ -173,15 +235,16 @@ defmodule Nola.Plugins.LastFm do
action = if np, do: "écoute ", else: "a écouté"
love = if track["loved"] != "0", do: "❤️"
count = if x = extended["userplaycount"], do: "x#{x} #{love}"
- tags = (get_in(extended, ["toptags", "tag"]) || [])
- |> Enum.map(fn(tag) -> tag["name"] end)
- |> Enum.filter(& &1)
- |> Enum.join(", ")
+
+ tags =
+ (get_in(extended, ["toptags", "tag"]) || [])
+ |> Enum.map(fn tag -> tag["name"] end)
+ |> Enum.filter(& &1)
+ |> Enum.join(", ")
[action, artist, name, count, tags, track["url"]]
|> Enum.filter(& &1)
|> Enum.map(&String.trim(&1))
|> Enum.join(" - ")
end
-
end
diff --git a/lib/plugins/link/github.ex b/lib/plugins/link/github.ex
index 77fa81f..fcd76a0 100644
--- a/lib/plugins/link/github.ex
+++ b/lib/plugins/link/github.ex
@@ -45,10 +45,14 @@ defmodule Nola.Plugins.Link.Github do
end
defp build_start(info) do
- parts = []
- |> maybe_add(info.disabled, " (disabled)")
- |> maybe_add(info.archived, " (archived)")
- |> maybe_add(info.source && info.source["full_name"] != info.full_name, " (⑂ #{info.source["full_name"]})")
+ parts =
+ []
+ |> maybe_add(info.disabled, " (disabled)")
+ |> maybe_add(info.archived, " (archived)")
+ |> maybe_add(
+ info.source && info.source["full_name"] != info.full_name,
+ " (⑂ #{info.source["full_name"]})"
+ )
"#{info.full_name}#{parts} - #{info.description}"
end
@@ -58,8 +62,9 @@ defmodule Nola.Plugins.Link.Github do
end
defp build_network(info) do
- lang = info.language && "#{info.language} - " || ""
- issues = info.open_issues_count && "#{info.open_issues_count} issues - " || ""
+ lang = (info.language && "#{info.language} - ") || ""
+ issues = (info.open_issues_count && "#{info.open_issues_count} issues - ") || ""
+
last_push =
if at = info.pushed_at do
{:ok, date, _} = DateTime.from_iso8601(at)
@@ -67,6 +72,7 @@ defmodule Nola.Plugins.Link.Github do
else
""
end
+
"#{lang}#{issues}#{info.stargazers_count} stars - #{info.subscribers_count} watchers - #{info.forks_count} forks#{last_push}"
end
diff --git a/lib/plugins/link/image.ex b/lib/plugins/link/image.ex
index cf3d9b0..2fb6862 100644
--- a/lib/plugins/link/image.ex
+++ b/lib/plugins/link/image.ex
@@ -6,7 +6,7 @@ defmodule Nola.Plugins.Link.Image do
def match(_, _), do: false
@impl true
- def post_match(_url, "image/"<>_, _header, _opts) do
+ def post_match(_url, "image/" <> _, _header, _opts) do
{:body, nil}
end
@@ -16,65 +16,77 @@ defmodule Nola.Plugins.Link.Image do
def post_expand(_url, bytes, _, opts) do
pil_process = Keyword.get(opts, :pil_process, {:pil, :"py@127.0.0.1"})
clip_ask_process = Keyword.get(opts, :clip_ask_process, {:clip_ask, :"py@127.0.0.1"})
- img2txt_process = Keyword.get(opts, :img2txt_process, {:image_to_text_vit_gpt2, :"py@127.0.0.1"})
-
- tasks = [
- Task.async(fn ->
- {:ok, pil} = GenServer.call(pil_process, {:run, bytes})
- pil = pil
- |> Enum.map(fn({k, v}) -> {String.to_atom(to_string(k)), v} end)
- pil
- end),
- Task.async(fn ->
- {:ok, descr} = GenServer.call(img2txt_process, {:run, bytes})
- {:img2txt, to_string(descr)}
- end),
- Task.async(fn ->
- {:ok, prompts} = GenServer.call(clip_ask_process, {:run, bytes})
-
- prompts = prompts
- |> Enum.sort_by(& elem(&1, 1), &>=/2)
- |> Enum.take(3)
- |> Enum.map(& to_string(elem(&1, 0)))
- |> Enum.join(", ")
- {:prompts, prompts}
+
+ img2txt_process =
+ Keyword.get(opts, :img2txt_process, {:image_to_text_vit_gpt2, :"py@127.0.0.1"})
+
+ tasks =
+ [
+ Task.async(fn ->
+ {:ok, pil} = GenServer.call(pil_process, {:run, bytes})
+
+ pil =
+ pil
+ |> Enum.map(fn {k, v} -> {String.to_atom(to_string(k)), v} end)
+
+ pil
+ end),
+ Task.async(fn ->
+ {:ok, descr} = GenServer.call(img2txt_process, {:run, bytes})
+ {:img2txt, to_string(descr)}
+ end),
+ Task.async(fn ->
+ {:ok, prompts} = GenServer.call(clip_ask_process, {:run, bytes})
+
+ prompts =
+ prompts
+ |> Enum.sort_by(&elem(&1, 1), &>=/2)
+ |> Enum.take(3)
+ |> Enum.map(&to_string(elem(&1, 0)))
+ |> Enum.join(", ")
+
+ {:prompts, prompts}
+ end)
+ ]
+ |> Task.yield_many(5000)
+ |> Enum.map(fn {task, res} ->
+ res || Task.shutdown(task, :brutal_kill)
end)
- ]
- |> Task.yield_many(5000)
- |> Enum.map(fn {task, res} ->
- res || Task.shutdown(task, :brutal_kill)
- end)
- results = Enum.into(List.flatten(for({:ok, value} <- tasks, do: value)), Map.new)
+ results = Enum.into(List.flatten(for({:ok, value} <- tasks, do: value)), Map.new())
img2txt = Map.get(results, :img2txt)
prompts = Map.get(results, :prompts)
- pil = if Map.get(results, :width) do
- animated = if Map.get(results, :animated), do: " animated", else: ""
- "#{Map.get(results, :width, 0)}x#{Map.get(results, :height, 0)}#{animated} — "
- else
- ""
- end
+ pil =
+ if Map.get(results, :width) do
+ animated = if Map.get(results, :animated), do: " animated", else: ""
+ "#{Map.get(results, :width, 0)}x#{Map.get(results, :height, 0)}#{animated} — "
+ else
+ ""
+ end
- descr = cond do
- img2txt && prompts ->
- "#{pil}#{prompts} — #{img2txt}"
- img2txt ->
- "#{pil}#{img2txt}"
- prompts ->
- "#{pil}#{prompts}"
- pil != "" ->
- "#{pil}"
- true ->
- nil
- end
+ descr =
+ cond do
+ img2txt && prompts ->
+ "#{pil}#{prompts} — #{img2txt}"
+
+ img2txt ->
+ "#{pil}#{img2txt}"
+
+ prompts ->
+ "#{pil}#{prompts}"
+
+ pil != "" ->
+ "#{pil}"
+
+ true ->
+ nil
+ end
if descr do
{:ok, "image: #{descr}"}
else
:error
end
-
end
-
end
diff --git a/lib/plugins/link/img_debrid_link.ex b/lib/plugins/link/img_debrid_link.ex
index a2972eb..b46c430 100644
--- a/lib/plugins/link/img_debrid_link.ex
+++ b/lib/plugins/link/img_debrid_link.ex
@@ -16,17 +16,17 @@ defmodule Nola.Plugins.Link.ImgDebridLink do
@impl true
def expand(_uri, %{id: ids}, _opts) do
- with \
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get("https://img.debrid-link.fr/api/v1/images/#{ids}/infos", [], []),
- {:ok, %{"success" => true, "value" => values}} <- Jason.decode(body)
- do
- items = for %{"name" => name, "url" => %{"direct" => direct_url}} <- values do
- "#{name}: #{direct_url}"
- end
+ with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <-
+ HTTPoison.get("https://img.debrid-link.fr/api/v1/images/#{ids}/infos", [], []),
+ {:ok, %{"success" => true, "value" => values}} <- Jason.decode(body) do
+ items =
+ for %{"name" => name, "url" => %{"direct" => direct_url}} <- values do
+ "#{name}: #{direct_url}"
+ end
+
{:ok, items}
else
_ -> :error
end
end
-
end
diff --git a/lib/plugins/link/imgur.ex b/lib/plugins/link/imgur.ex
index 9fe9354..49bbb7d 100644
--- a/lib/plugins/link/imgur.ex
+++ b/lib/plugins/link/imgur.ex
@@ -19,19 +19,24 @@ defmodule Nola.Plugins.Link.Imgur do
def match(uri = %URI{host: "imgur.io"}, arg) do
match(%URI{uri | host: "imgur.com"}, arg)
end
+
def match(uri = %URI{host: "i.imgur.io"}, arg) do
match(%URI{uri | host: "i.imgur.com"}, arg)
end
- def match(uri = %URI{host: "imgur.com", path: "/a/"<>album_id}, _) do
+
+ def match(uri = %URI{host: "imgur.com", path: "/a/" <> album_id}, _) do
{true, %{album_id: album_id}}
end
- def match(uri = %URI{host: "imgur.com", path: "/gallery/"<>album_id}, _) do
+
+ def match(uri = %URI{host: "imgur.com", path: "/gallery/" <> album_id}, _) do
{true, %{album_id: album_id}}
end
- def match(uri = %URI{host: "i.imgur.com", path: "/"<>image}, _) do
+
+ def match(uri = %URI{host: "i.imgur.com", path: "/" <> image}, _) do
[hash, _] = String.split(image, ".", parts: 2)
{true, %{image_id: hash}}
end
+
def match(_, _), do: false
@impl true
@@ -49,6 +54,7 @@ defmodule Nola.Plugins.Link.Imgur do
client_id = Keyword.get(Application.get_env(:nola, :imgur, []), :client_id, "42")
headers = [{"Authorization", "Client-ID #{client_id}"}]
options = []
+
case HTTPoison.get("https://api.imgur.com/3/image/#{image_id}", headers, options) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, json} = Jason.decode(body)
@@ -59,6 +65,7 @@ defmodule Nola.Plugins.Link.Imgur do
width = Map.get(data, "width")
size = Map.get(data, "size")
{:ok, "image, #{width}x#{height}, #{size} bytes #{nsfw}#{title}"}
+
other ->
:error
end
@@ -68,6 +75,7 @@ defmodule Nola.Plugins.Link.Imgur do
client_id = Keyword.get(Application.get_env(:nola, :imgur, []), :client_id, "42")
headers = [{"Authorization", "Client-ID #{client_id}"}]
options = []
+
case HTTPoison.get("https://api.imgur.com/3/album/#{album_id}", headers, options) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, json} = Jason.decode(body)
@@ -75,22 +83,31 @@ defmodule Nola.Plugins.Link.Imgur do
title = data["title"]
nsfw = data["nsfw"]
nsfw = if nsfw, do: "(NSFW) - ", else: ""
+
if data["images_count"] == 1 do
[image] = data["images"]
- title = if title || data["title"] do
- title = [title, data["title"]] |> Enum.filter(fn(x) -> x end) |> Enum.uniq() |> Enum.join(" — ")
- "#{title} — "
- else
- ""
- end
+
+ title =
+ if title || data["title"] do
+ title =
+ [title, data["title"]]
+ |> Enum.filter(fn x -> x end)
+ |> Enum.uniq()
+ |> Enum.join(" — ")
+
+ "#{title} — "
+ else
+ ""
+ end
+
{:ok, "#{nsfw}#{title}#{image["link"]}"}
else
title = if title, do: title, else: "Untitled album"
{:ok, "#{nsfw}#{title} - #{data["images_count"]} images"}
end
+
other ->
:error
end
end
-
end
diff --git a/lib/plugins/link/pdf.ex b/lib/plugins/link/pdf.ex
index e91dcc2..bb14594 100644
--- a/lib/plugins/link/pdf.ex
+++ b/lib/plugins/link/pdf.ex
@@ -6,7 +6,7 @@ defmodule Nola.Plugins.Link.PDF do
def match(_, _), do: false
@impl true
- def post_match(_url, "application/pdf"<>_, _header, _opts) do
+ def post_match(_url, "application/pdf" <> _, _header, _opts) do
{:file, nil}
end
@@ -16,24 +16,32 @@ defmodule Nola.Plugins.Link.PDF do
def post_expand(url, file, _, _) do
case System.cmd("pdftitle", ["-p", file]) do
{text, 0} ->
- text = text
- |> String.trim()
+ text =
+ text
+ |> String.trim()
if text == "" do
:error
else
basename = Path.basename(url, ".pdf")
- text = "[#{basename}] " <> text
- |> String.split("\n")
+
+ text =
+ ("[#{basename}] " <> text)
+ |> String.split("\n")
+
{:ok, text}
end
+
{_, 127} ->
- Logger.error("dependency `pdftitle` is missing, please install it: `pip3 install pdftitle`.")
+ Logger.error(
+ "dependency `pdftitle` is missing, please install it: `pip3 install pdftitle`."
+ )
+
:error
+
{error, code} ->
- Logger.warn("command `pdftitle` exited with status code #{code}:\n#{inspect error}")
+ Logger.warn("command `pdftitle` exited with status code #{code}:\n#{inspect(error)}")
:error
end
end
-
end
diff --git a/lib/plugins/link/redacted.ex b/lib/plugins/link/redacted.ex
index a7cfe74..0c14520 100644
--- a/lib/plugins/link/redacted.ex
+++ b/lib/plugins/link/redacted.ex
@@ -2,7 +2,10 @@ defmodule Nola.Plugins.Link.Redacted do
@behaviour Nola.Plugins.Link
@impl true
- def match(uri = %URI{host: "redacted.ch", path: "/torrent.php", query: query = "id="<>id}, _opts) do
+ def match(
+ uri = %URI{host: "redacted.ch", path: "/torrent.php", query: query = "id=" <> id},
+ _opts
+ ) do
%{"id" => id} = URI.decode_query(id)
{true, %{torrent: id}}
end
@@ -14,5 +17,4 @@ defmodule Nola.Plugins.Link.Redacted do
def expand(_uri, %{torrent: id}, _opts) do
end
-
end
diff --git a/lib/plugins/link/reddit.ex b/lib/plugins/link/reddit.ex
index 707e284..bd38084 100644
--- a/lib/plugins/link/reddit.ex
+++ b/lib/plugins/link/reddit.ex
@@ -6,14 +6,18 @@ defmodule Nola.Plugins.Link.Reddit do
case String.split(path, "/") do
["", "r", sub, "comments", post_id, _slug] ->
{true, %{mode: :post, path: path, sub: sub, post_id: post_id}}
+
["", "r", sub, "comments", post_id, _slug, ""] ->
{true, %{mode: :post, path: path, sub: sub, post_id: post_id}}
+
["", "r", sub, ""] ->
{true, %{mode: :sub, path: path, sub: sub}}
+
["", "r", sub] ->
{true, %{mode: :sub, path: path, sub: sub}}
-# ["", "u", user] ->
-# {true, %{mode: :user, path: path, user: user}}
+
+ # ["", "u", user] ->
+ # {true, %{mode: :user, path: path, user: user}}
_ ->
false
end
@@ -33,32 +37,47 @@ defmodule Nola.Plugins.Link.Reddit do
@impl true
def expand(_, %{mode: :sub, sub: sub}, _opts) do
url = "https://api.reddit.com/r/#{sub}/about"
+
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
- sr = Jason.decode!(body)
- |> Map.get("data")
- |> IO.inspect(limit: :infinity)
- description = Map.get(sr, "public_description")||Map.get(sr, "description", "")
- |> String.split("\n")
- |> List.first()
- name = if title = Map.get(sr, "title") do
- Map.get(sr, "display_name_prefixed") <> ": " <> title
- else
- Map.get(sr, "display_name_prefixed")
- end
- nsfw = if Map.get(sr, "over18") do
- "[NSFW] "
- else
- ""
- end
- quarantine = if Map.get(sr, "quarantine") do
- "[Quarantined] "
- else
- ""
- end
- count = "#{Map.get(sr, "subscribers")} subscribers, #{Map.get(sr, "active_user_count")} active"
+ sr =
+ Jason.decode!(body)
+ |> Map.get("data")
+ |> IO.inspect(limit: :infinity)
+
+ description =
+ Map.get(sr, "public_description") ||
+ Map.get(sr, "description", "")
+ |> String.split("\n")
+ |> List.first()
+
+ name =
+ if title = Map.get(sr, "title") do
+ Map.get(sr, "display_name_prefixed") <> ": " <> title
+ else
+ Map.get(sr, "display_name_prefixed")
+ end
+
+ nsfw =
+ if Map.get(sr, "over18") do
+ "[NSFW] "
+ else
+ ""
+ end
+
+ quarantine =
+ if Map.get(sr, "quarantine") do
+ "[Quarantined] "
+ else
+ ""
+ end
+
+ count =
+ "#{Map.get(sr, "subscribers")} subscribers, #{Map.get(sr, "active_user_count")} active"
+
preview = "#{quarantine}#{nsfw}#{name} — #{description} (#{count})"
{:ok, preview}
+
_ ->
:error
end
@@ -68,52 +87,66 @@ defmodule Nola.Plugins.Link.Reddit do
case HTTPoison.get("https://api.reddit.com#{path}?sr_detail=true") do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
json = Jason.decode!(body)
- op = List.first(json)
- |> Map.get("data")
- |> Map.get("children")
- |> List.first()
- |> Map.get("data")
- |> IO.inspect(limit: :infinity)
+
+ op =
+ List.first(json)
+ |> Map.get("data")
+ |> Map.get("children")
+ |> List.first()
+ |> Map.get("data")
+ |> IO.inspect(limit: :infinity)
+
sr = get_in(op, ["sr_detail", "display_name_prefixed"])
- {self?, url} = if Map.get(op, "selftext") == "" do
- {false, Map.get(op, "url")}
- else
- {true, nil}
- end
+
+ {self?, url} =
+ if Map.get(op, "selftext") == "" do
+ {false, Map.get(op, "url")}
+ else
+ {true, nil}
+ end
self_str = if(self?, do: "text", else: url)
up = Map.get(op, "ups")
down = Map.get(op, "downs")
comments = Map.get(op, "num_comments")
- nsfw = if Map.get(op, "over_18") do
- "[NSFW] "
- else
- ""
- end
- state = cond do
- Map.get(op, "hidden") -> "hidden"
- Map.get(op, "archived") -> "archived"
- Map.get(op, "locked") -> "locked"
- Map.get(op, "quarantine") -> "quarantined"
- Map.get(op, "removed_by") || Map.get(op, "removed_by_category") -> "removed"
- Map.get(op, "banned_by") -> "banned"
- Map.get(op, "pinned") -> "pinned"
- Map.get(op, "stickied") -> "stickied"
- true -> nil
- end
- flair = if flair = Map.get(op, "link_flair_text") do
- "[#{flair}] "
- else
- ""
- end
+
+ nsfw =
+ if Map.get(op, "over_18") do
+ "[NSFW] "
+ else
+ ""
+ end
+
+ state =
+ cond do
+ Map.get(op, "hidden") -> "hidden"
+ Map.get(op, "archived") -> "archived"
+ Map.get(op, "locked") -> "locked"
+ Map.get(op, "quarantine") -> "quarantined"
+ Map.get(op, "removed_by") || Map.get(op, "removed_by_category") -> "removed"
+ Map.get(op, "banned_by") -> "banned"
+ Map.get(op, "pinned") -> "pinned"
+ Map.get(op, "stickied") -> "stickied"
+ true -> nil
+ end
+
+ flair =
+ if flair = Map.get(op, "link_flair_text") do
+ "[#{flair}] "
+ else
+ ""
+ end
+
title = "#{nsfw}#{sr}: #{flair}#{Map.get(op, "title")}"
state_str = if(state, do: "#{state}, ")
- content = "by u/#{Map.get(op, "author")} - #{state_str}#{up} up, #{comments} comments - #{self_str}"
+
+ content =
+ "by u/#{Map.get(op, "author")} - #{state_str}#{up} up, #{comments} comments - #{self_str}"
{:ok, [title, content]}
+
err ->
:error
end
end
-
end
diff --git a/lib/plugins/link/scraper.ex b/lib/plugins/link/scraper.ex
index f5487e3..c30ae5f 100644
--- a/lib/plugins/link/scraper.ex
+++ b/lib/plugins/link/scraper.ex
@@ -1,5 +1,4 @@
defmodule Nola.Plugins.Link.Scraper do
-
defmodule UseScraper do
require Logger
@@ -7,28 +6,50 @@ defmodule Nola.Plugins.Link.Scraper do
base_url = Keyword.get(config, :base_url, "https://api.usescraper.com")
api_key = Keyword.get(config, :api_key, "unset api key")
options = Keyword.get(config, :http_options, [])
- headers = [{"user-agent", "nola, href@random.sh"},
- {"content-type", "application/json"},
- {"authorization", "Bearer " <> api_key}]
+
+ headers = [
+ {"user-agent", "nola, href@random.sh"},
+ {"content-type", "application/json"},
+ {"authorization", "Bearer " <> api_key}
+ ]
+
Logger.debug("scraper: use_scraper: get: #{url}")
+
with {:ok, json} <- Poison.encode(%{"url" => url, "format" => "html"}),
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.post("#{base_url}/scraper/scrape", json, headers, options),
- {:ok, %{"status" => "scraped", "html" => body, "meta" => meta = %{"fetchedUrlStatusCode" => 200}}} <- Poison.decode(body) do
+ {:ok, %HTTPoison.Response{status_code: 200, body: body}} <-
+ HTTPoison.post("#{base_url}/scraper/scrape", json, headers, options),
+ {:ok,
+ %{
+ "status" => "scraped",
+ "html" => body,
+ "meta" => meta = %{"fetchedUrlStatusCode" => 200}
+ }} <- Poison.decode(body) do
{:ok, body, meta}
else
- {:ok, %{"status" => "scraped", "text" => body, "meta" => meta = %{"fetchedUrlStatusCode" => code}}} ->
+ {:ok,
+ %{
+ "status" => "scraped",
+ "text" => body,
+ "meta" => meta = %{"fetchedUrlStatusCode" => code}
+ }} ->
Logger.error("scraper: use_scraper: scraper got http #{code} for #{url}")
status = Plug.Conn.Status.reason_atom(code)
{:error, status}
+
{:ok, %{"status" => "failed"}} ->
Logger.error("scraper: use_scraper: scraper service failed for #{url}")
{:error, :scrape_failed}
+
{:ok, %HTTPoison.Response{status_code: code, body: body}} ->
Logger.error("scraper: use_scraper: scraper service failed (http #{code}) for #{url}")
status = Plug.Conn.Status.reason_atom(code)
{:error, status}
+
{:error, %HTTPoison.Error{reason: reason}} ->
- Logger.error("scraper: use_scraper: scraper service failed (http #{inspect reason}) for #{url}")
+ Logger.error(
+ "scraper: use_scraper: scraper service failed (http #{inspect(reason)}) for #{url}"
+ )
+
{:error, reason}
end
end
@@ -36,10 +57,10 @@ defmodule Nola.Plugins.Link.Scraper do
def get(url) do
config = Keyword.get(Application.get_env(:nola, Nola.Plugins.Link, []), :scraper) || []
+
case config[:service] do
"usescraper" -> UseScraper.get(url, config[:config] || [])
_ -> {:error, :scraping_disabled}
end
end
-
end
diff --git a/lib/plugins/link/store.ex b/lib/plugins/link/store.ex
index 566cc9a..ea43070 100644
--- a/lib/plugins/link/store.ex
+++ b/lib/plugins/link/store.ex
@@ -26,5 +26,4 @@ defmodule Nola.Plugins.Link.Store do
[] -> nil
end
end
-
end
diff --git a/lib/plugins/link/twitter.ex b/lib/plugins/link/twitter.ex
index 48e6bae..ac2efe7 100644
--- a/lib/plugins/link/twitter.ex
+++ b/lib/plugins/link/twitter.ex
@@ -22,12 +22,15 @@ defmodule Nola.Plugins.Link.Twitter do
* `expand_quoted`: Add the quoted tweet instead of its URL. Default: true.
"""
- def match(uri = %URI{host: twitter, path: path}, _opts) when twitter in ["twitter.com", "m.twitter.com", "mobile.twitter.com"] do
+ def match(uri = %URI{host: twitter, path: path}, _opts)
+ when twitter in ["twitter.com", "m.twitter.com", "mobile.twitter.com"] do
case String.split(path, "/", parts: 4) do
["", _username, "status", status_id] ->
{status_id, _} = Integer.parse(status_id)
{true, %{status_id: status_id}}
- _ -> false
+
+ _ ->
+ false
end
end
@@ -62,56 +65,75 @@ defmodule Nola.Plugins.Link.Twitter do
# Format tweet text
text = expand_twitter_text(tweet, opts)
- text = if tweet.quoted_status do
- quote_url = link_tweet(tweet.quoted_status, opts, true)
- String.replace(text, quote_url, "")
- else
- text
- end
+
+ text =
+ if tweet.quoted_status do
+ quote_url = link_tweet(tweet.quoted_status, opts, true)
+ String.replace(text, quote_url, "")
+ else
+ text
+ end
+
text = Nola.Irc.Message.splitlong(text)
- reply_to = if tweet.in_reply_to_status_id do
- reply_url = link_tweet({tweet.in_reply_to_screen_name, tweet.in_reply_to_status_id}, opts)
- text = if tweet.in_reply_to_screen_name == tweet.user.screen_name, do: "continued from", else: "replying to"
- <<3, 15, " ↪ ", text::binary, " ", reply_url::binary, 3>>
- end
+ reply_to =
+ if tweet.in_reply_to_status_id do
+ reply_url = link_tweet({tweet.in_reply_to_screen_name, tweet.in_reply_to_status_id}, opts)
- quoted = if tweet.quoted_status do
- full_text = tweet.quoted_status
- |> expand_twitter_text(opts)
- |> Nola.Irc.Message.splitlong_with_prefix(">")
+ text =
+ if tweet.in_reply_to_screen_name == tweet.user.screen_name,
+ do: "continued from",
+ else: "replying to"
- head = format_tweet_header(tweet.quoted_status, opts, details: false, prefix: "↓ quoting")
+ <<3, 15, " ↪ ", text::binary, " ", reply_url::binary, 3>>
+ end
- [head | full_text]
- else
- []
- end
+ quoted =
+ if tweet.quoted_status do
+ full_text =
+ tweet.quoted_status
+ |> expand_twitter_text(opts)
+ |> Nola.Irc.Message.splitlong_with_prefix(">")
+
+ head = format_tweet_header(tweet.quoted_status, opts, details: false, prefix: "↓ quoting")
+
+ [head | full_text]
+ else
+ []
+ end
- #<<2, "#{tweet.user.name} (@#{tweet.user.screen_name})", 2, " ", 3, 61, "#{foot} #{nitter_link}", 3>>, reply_to] ++ text ++ quoted
+ # <<2, "#{tweet.user.name} (@#{tweet.user.screen_name})", 2, " ", 3, 61, "#{foot} #{nitter_link}", 3>>, reply_to] ++ text ++ quoted
+
+ text =
+ ([head, reply_to | text] ++ quoted)
+ |> Enum.filter(& &1)
- text = [head, reply_to | text] ++ quoted
- |> Enum.filter(& &1)
{:ok, text}
end
defp expand_twitter_text(tweet, _opts) do
- text = Enum.reduce(tweet.entities.urls, tweet.full_text, fn(entity, text) ->
- String.replace(text, entity.url, entity.expanded_url)
- end)
+ text =
+ Enum.reduce(tweet.entities.urls, tweet.full_text, fn entity, text ->
+ String.replace(text, entity.url, entity.expanded_url)
+ end)
+
extended = tweet.extended_entities || %{media: []}
- text = Enum.reduce(extended.media, text, fn(entity, text) ->
- url = Enum.filter(extended.media, fn(e) -> entity.url == e.url end)
- |> Enum.map(fn(e) ->
- cond do
- e.type == "video" -> e.expanded_url
- true -> e.media_url_https
- end
+
+ text =
+ Enum.reduce(extended.media, text, fn entity, text ->
+ url =
+ Enum.filter(extended.media, fn e -> entity.url == e.url end)
+ |> Enum.map(fn e ->
+ cond do
+ e.type == "video" -> e.expanded_url
+ true -> e.media_url_https
+ end
+ end)
+ |> Enum.join(" ")
+
+ String.replace(text, entity.url, url)
end)
- |> Enum.join(" ")
- String.replace(text, entity.url, url)
- end)
- |> HtmlEntities.decode()
+ |> HtmlEntities.decode()
end
defp format_tweet_header(tweet, opts, format_opts \\ []) do
@@ -134,25 +156,28 @@ defmodule Nola.Plugins.Link.Twitter do
replies = if tweet.reply_count && tweet.reply_count > 0, do: "#{tweet.reply_count} Reps"
dmcad = if tweet.withheld_copyright, do: <<3, 52, "DMCA", 3>>
- withheld_local = if tweet.withheld_in_countries && length(tweet.withheld_in_countries) > 0 do
- "Withheld in #{length(tweet.withheld_in_countries)} countries"
- end
+
+ withheld_local =
+ if tweet.withheld_in_countries && length(tweet.withheld_in_countries) > 0 do
+ "Withheld in #{length(tweet.withheld_in_countries)} countries"
+ end
verified = if tweet.user.verified, do: <<3, 51, "✔", 3>>
- meta = if details do
- [verified, nsfw, formatted_time, dmcad, withheld_local, rts, qrts, likes, replies]
- else
- [verified, nsfw, formatted_time, dmcad, withheld_local]
- end
+ meta =
+ if details do
+ [verified, nsfw, formatted_time, dmcad, withheld_local, rts, qrts, likes, replies]
+ else
+ [verified, nsfw, formatted_time, dmcad, withheld_local]
+ end
- meta = meta
- |> Enum.filter(& &1)
- |> Enum.join(" - ")
+ meta =
+ meta
+ |> Enum.filter(& &1)
+ |> Enum.join(" - ")
meta = <<3, 15, meta::binary, " → #{link}", 3>>
<<author::binary, " — ", meta::binary>>
end
-
end
diff --git a/lib/plugins/link/youtube.ex b/lib/plugins/link/youtube.ex
index 0114940..adf9337 100644
--- a/lib/plugins/link/youtube.ex
+++ b/lib/plugins/link/youtube.ex
@@ -17,11 +17,12 @@ defmodule Nola.Plugins.Link.YouTube do
"""
@impl true
- def match(uri = %URI{host: yt, path: "/watch", query: "v="<>video_id}, _opts) when yt in ["youtube.com", "www.youtube.com"] do
+ def match(uri = %URI{host: yt, path: "/watch", query: "v=" <> video_id}, _opts)
+ when yt in ["youtube.com", "www.youtube.com"] do
{true, %{video_id: video_id}}
end
- def match(%URI{host: "youtu.be", path: "/"<>video_id}, _opts) do
+ def match(%URI{host: "youtu.be", path: "/" <> video_id}, _opts) do
{true, %{video_id: video_id}}
end
@@ -33,40 +34,58 @@ defmodule Nola.Plugins.Link.YouTube do
@impl true
def expand(_uri, %{video_id: video_id}, opts) do
key = Application.get_env(:nola, :youtube)[:api_key]
+
params = %{
"part" => "snippet,contentDetails,statistics",
"id" => video_id,
"key" => key
}
+
headers = []
options = [params: params]
+
case HTTPoison.get("https://www.googleapis.com/youtube/v3/videos", [], options) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
case Jason.decode(body) do
{:ok, json} ->
item = List.first(json["items"])
+
if item do
snippet = item["snippet"]
- duration = item["contentDetails"]["duration"] |> String.replace("PT", "") |> String.downcase
- date = snippet["publishedAt"]
- |> DateTime.from_iso8601()
- |> elem(1)
- |> Timex.format("{relative}", :relative)
- |> elem(1)
-
- line = if host = Keyword.get(opts, :invidious) do
- ["-> https://#{host}/watch?v=#{video_id}"]
- else
+
+ duration =
+ item["contentDetails"]["duration"]
+ |> String.replace("PT", "")
+ |> String.downcase()
+
+ date =
+ snippet["publishedAt"]
+ |> DateTime.from_iso8601()
+ |> elem(1)
+ |> Timex.format("{relative}", :relative)
+ |> elem(1)
+
+ line =
+ if host = Keyword.get(opts, :invidious) do
+ ["-> https://#{host}/watch?v=#{video_id}"]
+ else
[]
- end
- {:ok, line ++ ["#{snippet["title"]}", "— #{duration} — uploaded by #{snippet["channelTitle"]} — #{date}"
- <> " — #{item["statistics"]["viewCount"]} views, #{item["statistics"]["likeCount"]} likes"]}
+ end
+
+ {:ok,
+ line ++
+ [
+ "#{snippet["title"]}",
+ "— #{duration} — uploaded by #{snippet["channelTitle"]} — #{date}" <>
+ " — #{item["statistics"]["viewCount"]} views, #{item["statistics"]["likeCount"]} likes"
+ ]}
else
:error
end
- _ -> :error
+
+ _ ->
+ :error
end
end
end
-
end
diff --git a/lib/plugins/logger.ex b/lib/plugins/logger.ex
index 46c2a5b..1418ddc 100644
--- a/lib/plugins/logger.ex
+++ b/lib/plugins/logger.ex
@@ -32,52 +32,53 @@ defmodule Nola.Plugins.Logger do
end
def handle_info(info, state) do
- Logger.debug("logger_plugin: unhandled info: #{inspect info}")
+ Logger.debug("logger_plugin: unhandled info: #{inspect(info)}")
{:noreply, state}
end
def log(entry, state) do
case Couch.post(@couch_db, format_to_db(entry)) do
{:ok, id, _rev} ->
- Logger.debug("logger_plugin: saved: #{inspect id}")
+ Logger.debug("logger_plugin: saved: #{inspect(id)}")
state
+
error ->
- Logger.error("logger_plugin: save failed: #{inspect error}")
+ Logger.error("logger_plugin: save failed: #{inspect(error)}")
end
rescue
e ->
- Logger.error("logger_plugin: rescued processing for #{inspect entry}: #{inspect e}")
+ Logger.error("logger_plugin: rescued processing for #{inspect(entry)}: #{inspect(e)}")
Logger.error(Exception.format(:error, e, __STACKTRACE__))
state
catch
e, b ->
- Logger.error("logger_plugin: catched processing for #{inspect entry}: #{inspect e}")
+ Logger.error("logger_plugin: catched processing for #{inspect(entry)}: #{inspect(e)}")
Logger.error(Exception.format(e, b, __STACKTRACE__))
state
end
def format_to_db(msg = %Nola.Message{id: id}) do
- channel = cond do
- msg.channel -> msg.channel
- msg.account -> msg.account.id
- msg.sender -> msg.sender.nick
- true -> nil
- end
+ channel =
+ cond do
+ msg.channel -> msg.channel
+ msg.account -> msg.account.id
+ msg.sender -> msg.sender.nick
+ true -> nil
+ end
- id = [msg.network, channel, id]
- |> Enum.filter(& &1)
- |> Enum.join(":")
+ id =
+ [msg.network, channel, id]
+ |> Enum.filter(& &1)
+ |> Enum.join(":")
- %{"_id" => id,
+ %{
+ "_id" => id,
"type" => "nola.message:v1",
"object" => %Nola.Message{msg | meta: Map.delete(msg.meta, :from)}
}
end
def format_to_db(anything) do
- %{"_id" => FlakeId.get(),
- "type" => "object",
- "object" => anything}
+ %{"_id" => FlakeId.get(), "type" => "object", "object" => anything}
end
-
end
diff --git a/lib/plugins/quatre_cent_vingt.ex b/lib/plugins/quatre_cent_vingt.ex
index 6b3cc46..f530446 100644
--- a/lib/plugins/quatre_cent_vingt.ex
+++ b/lib/plugins/quatre_cent_vingt.ex
@@ -10,11 +10,15 @@ defmodule Nola.Plugins.QuatreCentVingt do
"""
@achievements %{
- 1 => ["[le premier… il faut bien commencer un jour]"],
- 10 => ["T'en es seulement à 10 ? ╭∩╮(Ο_Ο)╭∩╮"],
- 42 => ["Bravo, et est-ce que autant de pétards t'on aidés à trouver la Réponse ? ٩(- ̮̮̃-̃)۶ [42]"],
- 100 => ["°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ 100 °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸"],
- 115 => [" ۜ\(סּںסּَ` )/ۜ 115!!"]
+ 1 => ["[le premier… il faut bien commencer un jour]"],
+ 10 => ["T'en es seulement à 10 ? ╭∩╮(Ο_Ο)╭∩╮"],
+ 42 => [
+ "Bravo, et est-ce que autant de pétards t'on aidés à trouver la Réponse ? ٩(- ̮̮̃-̃)۶ [42]"
+ ],
+ 100 => [
+ "°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ 100 °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸"
+ ],
+ 115 => [" ۜ\(סּںסּَ` )/ۜ 115!!"]
}
@emojis [
@@ -23,7 +27,7 @@ defmodule Nola.Plugins.QuatreCentVingt do
"~~o∞~~",
"*\\o/*",
"**\\o/**",
- "*ô*",
+ "*ô*"
]
@coeffs Range.new(1, 100)
@@ -34,75 +38,102 @@ defmodule Nola.Plugins.QuatreCentVingt do
def init(_) do
for coeff <- @coeffs do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:#{420*coeff}", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:#{420 * coeff}", plugin: __MODULE__)
end
- {:ok, _} = Registry.register(Nola.PubSub, "account", [plugin: __MODULE__])
- dets_filename = (Nola.data_path() <> "/420.dets") |> String.to_charlist
- {:ok, dets} = :dets.open_file(dets_filename, [{:type,:bag},{:repair,:force}])
+
+ {:ok, _} = Registry.register(Nola.PubSub, "account", plugin: __MODULE__)
+ dets_filename = (Nola.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
qvc = to_string(420 * coeff)
- def handle_info({:irc, :trigger, unquote(qvc), m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :bang}}}, dets) do
+
+ def handle_info(
+ {:irc, :trigger, unquote(qvc),
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [], type: :bang}}},
+ dets
+ ) do
{count, last} = get_statistics_for_nick(dets, m.account.id)
count = count + unquote(coeff)
text = achievement_text(count)
- now = DateTime.to_unix(DateTime.utc_now())-1 # this is ugly
+ # this is ugly
+ now = DateTime.to_unix(DateTime.utc_now()) - 1
+
for i <- Range.new(1, unquote(coeff)) do
- :ok = :dets.insert(dets, {m.account.id, now+i})
- end
- last_s = if last do
- last_s = format_relative_timestamp(last)
- " (le dernier était #{last_s})"
- else
- ""
+ :ok = :dets.insert(dets, {m.account.id, now + i})
end
+
+ last_s =
+ if last do
+ last_s = format_relative_timestamp(last)
+ " (le dernier était #{last_s})"
+ else
+ ""
+ end
+
m.replyfun.("#{m.sender.nick} 420 +#{unquote(coeff)} #{text}#{last_s}")
{:noreply, dets}
end
end
- def handle_info({:irc, :trigger, "420", m = %Nola.Message{trigger: %Nola.Trigger{args: [nick], type: :bang}}}, dets) do
+ def handle_info(
+ {:irc, :trigger, "420",
+ m = %Nola.Message{trigger: %Nola.Trigger{args: [nick], type: :bang}}},
+ dets
+ ) do
account = Nola.Account.find_by_nick(m.network, nick)
+
if account do
- text = case get_statistics_for_nick(dets, m.account.id) do
- {0, _} -> "#{nick} n'a jamais !420 ... honte à lui."
- {count, last} ->
- last_s = format_relative_timestamp(last)
- "#{nick} 420: total #{count}, le dernier #{last_s}"
- end
+ text =
+ case get_statistics_for_nick(dets, m.account.id) do
+ {0, _} ->
+ "#{nick} n'a jamais !420 ... honte à lui."
+
+ {count, last} ->
+ last_s = format_relative_timestamp(last)
+ "#{nick} 420: total #{count}, le dernier #{last_s}"
+ end
+
m.replyfun.(text)
else
m.replyfun.("je connais pas de #{nick}")
end
+
{:noreply, dets}
end
# Account
def handle_info({:account_change, old_id, new_id}, dets) do
spec = [{{:"$1", :_}, [{:==, :"$1", {:const, old_id}}], [:"$_"]}]
- Util.ets_mutate_select_each(:dets, dets, spec, fn(table, obj) ->
+
+ Util.ets_mutate_select_each(:dets, dets, spec, fn table, obj ->
rename_object_owner(table, obj, new_id)
end)
+
{:noreply, dets}
end
# Account: move from nick to account id
def handle_info({:accounts, accounts}, dets) do
- for x={:account, _net, _chan, _nick, _account_id} <- accounts do
+ for x = {:account, _net, _chan, _nick, _account_id} <- accounts do
handle_info(x, dets)
end
+
{:noreply, dets}
end
+
def handle_info({:account, _net, _chan, nick, account_id}, dets) do
nick = String.downcase(nick)
spec = [{{:"$1", :_}, [{:==, :"$1", {:const, nick}}], [:"$_"]}]
- Util.ets_mutate_select_each(:dets, dets, spec, fn(table, obj) ->
+
+ Util.ets_mutate_select_each(:dets, dets, spec, fn table, obj ->
Logger.debug("account:: merging #{nick} -> #{account_id}")
rename_object_owner(table, obj, account_id)
end)
+
{:noreply, dets}
end
@@ -115,23 +146,26 @@ defmodule Nola.Plugins.QuatreCentVingt do
:dets.insert(table, {account_id, at})
end
-
defp format_relative_timestamp(timestamp) do
alias Timex.Format.DateTime.Formatters
alias Timex.Timezone
- date = timestamp
- |> DateTime.from_unix!
- |> Timezone.convert("Europe/Paris")
- {:ok, relative} = Formatters.Relative.relative_to(date, Timex.now("Europe/Paris"), "{relative}", "fr")
+ date =
+ timestamp
+ |> DateTime.from_unix!()
+ |> Timezone.convert("Europe/Paris")
+
+ {:ok, relative} =
+ Formatters.Relative.relative_to(date, Timex.now("Europe/Paris"), "{relative}", "fr")
+
{:ok, detail} = Formatters.Default.lformat(date, " ({h24}:{m})", "fr")
relative <> detail
end
defp get_statistics_for_nick(dets, acct) do
- qvc = :dets.lookup(dets, acct) |> Enum.sort
- count = Enum.reduce(qvc, 0, fn(_, acc) -> acc + 1 end)
+ qvc = :dets.lookup(dets, acct) |> Enum.sort()
+ count = Enum.reduce(qvc, 0, fn _, acc -> acc + 1 end)
{_, last} = List.last(qvc) || {nil, nil}
{count, last}
end
@@ -145,5 +179,4 @@ defmodule Nola.Plugins.QuatreCentVingt do
emoji = Enum.random(@emojis)
"#{emoji} [#{count}]"
end
-
end
diff --git a/lib/plugins/radio_france.ex b/lib/plugins/radio_france.ex
index d95c54a..e9adc4e 100644
--- a/lib/plugins/radio_france.ex
+++ b/lib/plugins/radio_france.ex
@@ -23,27 +23,45 @@ defmodule Nola.Plugins.RadioFrance do
regopts = [plugin: __MODULE__]
{:ok, _} = Registry.register(Nola.PubSub, "trigger:radiofrance", regopts)
{:ok, _} = Registry.register(Nola.PubSub, "trigger:rf", regopts)
+
for s <- @shortcuts do
{:ok, _} = Registry.register(Nola.PubSub, "trigger:#{s}", regopts)
end
+
{:ok, nil}
end
- def handle_info({:irc, :trigger, "rf", m = %Nola.Message{trigger: %Nola.Trigger{type: :bang}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "rf", m = %Nola.Message{trigger: %Nola.Trigger{type: :bang}}},
+ state
+ ) do
handle_info({:irc, :trigger, "radiofrance", m}, state)
end
- def handle_info({:irc, :trigger, @trigger, m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: []}}}, state) do
+ def handle_info(
+ {:irc, :trigger, @trigger,
+ m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: []}}},
+ state
+ ) do
m.replyfun.("radiofrance: précisez la station!")
{:noreply, state}
end
- def handle_info({:irc, :trigger, @trigger, m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}}, state) do
+ def handle_info(
+ {:irc, :trigger, @trigger,
+ m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}},
+ state
+ ) do
now(args_to_station(args), m)
{:noreply, state}
end
- def handle_info({:irc, :trigger, trigger, m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}}, state) when trigger in @shortcuts do
+ def handle_info(
+ {:irc, :trigger, trigger,
+ m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}},
+ state
+ )
+ when trigger in @shortcuts do
now(args_to_station([trigger | args]), m)
{:noreply, state}
end
@@ -56,7 +74,7 @@ defmodule Nola.Plugins.RadioFrance do
end
def handle_info(info, state) do
- Logger.debug("unhandled info: #{inspect info}")
+ Logger.debug("unhandled info: #{inspect(info)}")
{:noreply, state}
end
@@ -67,7 +85,7 @@ defmodule Nola.Plugins.RadioFrance do
song? = !!get_in(json, ["now", "song"])
station = reformat_station_name(get_in(json, ["now", "stationName"]))
now_title = get_in(json, ["now", "firstLine", "title"])
- now_subtitle = get_in(json, ["now", "secondLine", "title"])
+ now_subtitle = get_in(json, ["now", "secondLine", "title"])
next_title = get_in(json, ["next", "firstLine", "title"])
next_subtitle = get_in(json, ["next", "secondLine", "title"])
next_song? = !!get_in(json, ["next", "song"])
@@ -76,21 +94,25 @@ defmodule Nola.Plugins.RadioFrance do
now = format_title(song?, now_title, now_subtitle)
prefix = if song?, do: "🎶", else: "🎤"
m.replyfun.("#{prefix} #{station}: #{now}")
-
+
next = format_title(song?, next_title, next_subtitle)
+
if next do
- next_prefix = if next_at do
- next_date = DateTime.from_unix!(next_at)
- in_seconds = DateTime.diff(next_date, DateTime.utc_now())
- in_minutes = ceil(in_seconds / 60)
- if in_minutes >= 5 do
- if next_song?, do: "#{in_minutes}m 🔜", else: "dans #{in_minutes} minutes:"
+ next_prefix =
+ if next_at do
+ next_date = DateTime.from_unix!(next_at)
+ in_seconds = DateTime.diff(next_date, DateTime.utc_now())
+ in_minutes = ceil(in_seconds / 60)
+
+ if in_minutes >= 5 do
+ if next_song?, do: "#{in_minutes}m 🔜", else: "dans #{in_minutes} minutes:"
+ else
+ if next_song?, do: "🔜", else: "suivi de:"
+ end
else
- if next_song?, do: "🔜", else: "suivi de:"
+ if next_song?, do: "🔜", else: "à suivre:"
end
- else
- if next_song?, do: "🔜", else: "à suivre:"
- end
+
m.replyfun.("#{next_prefix} #{next}")
end
@@ -117,9 +139,11 @@ defmodule Nola.Plugins.RadioFrance do
defp format_title(_, nil, nil) do
nil
end
+
defp format_title(true, title, artist) do
[artist, title] |> Enum.filter(& &1) |> Enum.join(" - ")
end
+
defp format_title(false, show, section) do
[show, section] |> Enum.filter(& &1) |> Enum.join(": ")
end
@@ -129,5 +153,4 @@ defmodule Nola.Plugins.RadioFrance do
|> String.replace("france", "france ")
|> String.replace("_", " ")
end
-
end
diff --git a/lib/plugins/say.ex b/lib/plugins/say.ex
index 114ca64..3ccd0a4 100644
--- a/lib/plugins/say.ex
+++ b/lib/plugins/say.ex
@@ -1,5 +1,4 @@
defmodule Nola.Plugins.Say do
-
def irc_doc do
"""
# say
@@ -25,31 +24,39 @@ defmodule Nola.Plugins.Say do
{:ok, nil}
end
- def handle_info({:irc, :trigger, "say", m = %{trigger: %{type: :bang, args: [target | text]}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "say", m = %{trigger: %{type: :bang, args: [target | text]}}},
+ state
+ ) do
text = Enum.join(text, " ")
say_for(m.account, target, text, true)
{:noreply, state}
- end
+ end
- def handle_info({:irc, :trigger, "asay", m = %{trigger: %{type: :bang, args: [target | text]}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "asay", m = %{trigger: %{type: :bang, args: [target | text]}}},
+ state
+ ) do
text = Enum.join(text, " ")
say_for(m.account, target, text, false)
{:noreply, state}
- end
+ end
- def handle_info({:irc, :text, m = %{text: "say "<>rest}}, state) do
+ def handle_info({:irc, :text, m = %{text: "say " <> rest}}, state) do
case String.split(rest, " ", parts: 2) do
[target, text] -> say_for(m.account, target, text, true)
_ -> nil
end
+
{:noreply, state}
end
- def handle_info({:irc, :text, m = %{text: "asay "<>rest}}, state) do
+ def handle_info({:irc, :text, m = %{text: "asay " <> rest}}, state) do
case String.split(rest, " ", parts: 2) do
[target, text] -> say_for(m.account, target, text, false)
_ -> nil
end
+
{:noreply, state}
end
@@ -60,7 +67,9 @@ defmodule Nola.Plugins.Say do
defp say_for(account, target, text, with_nick?) do
for {net, chan} <- Nola.Membership.of_account(account) do
chan2 = String.replace(chan, "#", "")
- if (target == "#{net}/#{chan}" || target == "#{net}/#{chan2}" || target == chan || target == chan2) do
+
+ if target == "#{net}/#{chan}" || target == "#{net}/#{chan2}" || target == chan ||
+ target == chan2 do
if with_nick? do
Nola.Irc.send_message_as(account, net, chan, text)
else
@@ -69,5 +78,4 @@ defmodule Nola.Plugins.Say do
end
end
end
-
end
diff --git a/lib/plugins/script.ex b/lib/plugins/script.ex
index c8d00a9..0a65627 100644
--- a/lib/plugins/script.ex
+++ b/lib/plugins/script.ex
@@ -23,20 +23,24 @@ defmodule Nola.Plugins.Script do
end
def init([]) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:script", [plugin: __MODULE__])
- dets_filename = (Nola.data_path() <> "/" <> "scripts.dets") |> String.to_charlist
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:script", plugin: __MODULE__)
+ dets_filename = (Nola.data_path() <> "/" <> "scripts.dets") |> String.to_charlist()
{:ok, dets} = :dets.open_file(dets_filename, [])
{:ok, %{dets: dets}}
end
- def handle_info({:irc, :trigger, "script", m = %{trigger: %{type: :plus, args: [name | args]}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "script", m = %{trigger: %{type: :plus, args: [name | args]}}},
+ state
+ ) do
end
def handle_info({:irc, :trigger, "script", m = %{trigger: %{type: :minus, args: args}}}, state) do
case args do
- ["del", name] -> :ok #prout
- [name] -> :ok#stop
+ # prout
+ ["del", name] -> :ok
+ # stop
+ [name] -> :ok
end
end
-
end
diff --git a/lib/plugins/seen.ex b/lib/plugins/seen.ex
index 045702c..cff0928 100644
--- a/lib/plugins/seen.ex
+++ b/lib/plugins/seen.ex
@@ -6,6 +6,7 @@ defmodule Nola.Plugins.Seen do
"""
def irc_doc, do: @moduledoc
+
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@@ -19,7 +20,11 @@ defmodule Nola.Plugins.Seen do
{:ok, %{dets: dets}}
end
- def handle_info({:irc, :trigger, "seen", m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: [nick]}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "seen",
+ m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: [nick]}}},
+ state
+ ) do
witness(m, state)
m.replyfun.(last_seen(m.channel, nick, state))
{:noreply, state}
@@ -43,17 +48,20 @@ defmodule Nola.Plugins.Seen do
defp last_seen(channel, nick, %{dets: dets}) do
case :dets.lookup(dets, {channel, nick}) do
[{_, date, text}] ->
- diff = round(DateTime.diff(DateTime.utc_now(), date)/60)
+ diff = round(DateTime.diff(DateTime.utc_now(), date) / 60)
+
cond do
diff >= 30 ->
duration = Timex.Duration.from_minutes(diff)
format = Timex.Format.Duration.Formatter.lformat(duration, "fr", :humanized)
"#{nick} a parlé pour la dernière fois il y a #{format}: “#{text}”"
- true -> "#{nick} est là..."
+
+ true ->
+ "#{nick} est là..."
end
+
[] ->
"je ne connais pas de #{nick}"
end
end
-
end
diff --git a/lib/plugins/sms.ex b/lib/plugins/sms.ex
index 8dd15ad..713ac3f 100644
--- a/lib/plugins/sms.ex
+++ b/lib/plugins/sms.ex
@@ -8,9 +8,10 @@ defmodule Nola.Plugins.Sms do
def irc_doc, do: @moduledoc
require Logger
- def incoming(from, "enable "<>key) do
+ def incoming(from, "enable " <> key) do
key = String.trim(key)
account = Nola.Account.find_meta_account("sms-validation-code", String.downcase(key))
+
if account do
net = Nola.Account.get_meta(account, "sms-validation-target")
Nola.Account.put_meta(account, "sms-number", from)
@@ -24,15 +25,21 @@ defmodule Nola.Plugins.Sms do
def incoming(from, message) do
account = Nola.Account.find_meta_account("sms-number", from)
+
if account do
- reply_fun = fn(text) ->
+ reply_fun = fn text ->
send_sms(from, text)
end
- trigger_text = if Enum.any?(Nola.Irc.Connection.triggers(), fn({trigger, _}) -> String.starts_with?(message, trigger) end) do
- message
- else
- "!"<>message
- end
+
+ trigger_text =
+ if Enum.any?(Nola.Irc.Connection.triggers(), fn {trigger, _} ->
+ String.starts_with?(message, trigger)
+ end) do
+ message
+ else
+ "!" <> message
+ end
+
message = %Nola.Message{
id: FlakeId.get(),
transport: :sms,
@@ -44,7 +51,8 @@ defmodule Nola.Plugins.Sms do
replyfun: reply_fun,
trigger: Nola.Irc.Connection.extract_trigger(trigger_text)
}
- Logger.debug("converted sms to message: #{inspect message}")
+
+ Logger.debug("converted sms to message: #{inspect(message)}")
Nola.Irc.Connection.publish(message, ["messages:sms"])
message
end
@@ -69,53 +77,70 @@ defmodule Nola.Plugins.Sms do
def send_sms(number, text) do
url = path("/virtualNumbers/#{my_number()}/jobs")
- body = %{
- "message" => text,
- "receivers" => [number],
- #"senderForResponse" => true,
- #"noStopClause" => true,
- "charset" => "UTF-8",
- "coding" => "8bit"
- } |> Poison.encode!()
+
+ body =
+ %{
+ "message" => text,
+ "receivers" => [number],
+ # "senderForResponse" => true,
+ # "noStopClause" => true,
+ "charset" => "UTF-8",
+ "coding" => "8bit"
+ }
+ |> Poison.encode!()
+
headers = [{"content-type", "application/json"}] ++ sign("POST", url, body)
options = []
+
case HTTPoison.post(url, body, headers, options) do
- {:ok, %HTTPoison.Response{status_code: 200}} -> :ok
+ {:ok, %HTTPoison.Response{status_code: 200}} ->
+ :ok
+
{:ok, %HTTPoison.Response{status_code: code} = resp} ->
- Logger.error("SMS Error: #{inspect resp}")
+ Logger.error("SMS Error: #{inspect(resp)}")
{:error, code}
- {:error, error} -> {:error, error}
+
+ {:error, error} ->
+ {:error, error}
end
end
def init([]) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:sms", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:sms", plugin: __MODULE__)
:ok = register_ovh_callback()
{:ok, %{}}
:ignore
end
- def handle_info({:irc, :trigger, "sms", m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: [nick | text]}}}, state) do
- with \
- {:tree, false} <- {:tree, m.sender.nick == "Tree"},
- {_, %Nola.Account{} = account} <- {:account, Nola.Account.find_always_by_nick(m.network, m.channel, nick)},
- {_, number} when not is_nil(number) <- {:number, Nola.Account.get_meta(account, "sms-number")}
- do
+ def handle_info(
+ {:irc, :trigger, "sms",
+ m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: [nick | text]}}},
+ state
+ ) do
+ with {:tree, false} <- {:tree, m.sender.nick == "Tree"},
+ {_, %Nola.Account{} = account} <-
+ {:account, Nola.Account.find_always_by_nick(m.network, m.channel, nick)},
+ {_, number} when not is_nil(number) <-
+ {:number, Nola.Account.get_meta(account, "sms-number")} do
text = Enum.join(text, " ")
- sender = if m.channel do
- "#{m.channel} <#{m.sender.nick}> "
- else
- "<#{m.sender.nick}> "
- end
- case send_sms(number, sender<>text) do
+
+ sender =
+ if m.channel do
+ "#{m.channel} <#{m.sender.nick}> "
+ else
+ "<#{m.sender.nick}> "
+ end
+
+ case send_sms(number, sender <> text) do
:ok -> m.replyfun.("sent!")
- {:error, error} -> m.replyfun.("not sent, error: #{inspect error}")
+ {:error, error} -> m.replyfun.("not sent, error: #{inspect(error)}")
end
else
{:tree, _} -> m.replyfun.("Tree: va en enfer")
{:account, _} -> m.replyfun.("#{nick} not known")
{:number, _} -> m.replyfun.("#{nick} have not enabled sms")
end
+
{:noreply, state}
end
@@ -125,19 +150,26 @@ defmodule Nola.Plugins.Sms do
defp register_ovh_callback() do
url = path()
- body = %{
- "callBack" =>NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback),
- "smsResponse" => %{
- "cgiUrl" => NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback),
- "responseType" => "cgi"
+
+ body =
+ %{
+ "callBack" => NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback),
+ "smsResponse" => %{
+ "cgiUrl" => NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback),
+ "responseType" => "cgi"
+ }
}
- } |> Poison.encode!()
+ |> Poison.encode!()
+
headers = [{"content-type", "application/json"}] ++ sign("PUT", url, body)
options = []
+
case HTTPoison.put(url, body, headers, options) do
{:ok, %HTTPoison.Response{status_code: 200}} ->
:ok
- error -> error
+
+ error ->
+ error
end
end
@@ -147,8 +179,13 @@ defmodule Nola.Plugins.Sms do
ck = env(:consumer_key)
sign = Enum.join([as, ck, String.upcase(method), url, body, ts], "+")
sign_hex = :crypto.hash(:sha, sign) |> Base.encode16(case: :lower)
- headers = [{"X-OVH-Application", env(:app_key)}, {"X-OVH-Timestamp", ts},
- {"X-OVH-Signature", "$1$"<>sign_hex}, {"X-Ovh-Consumer", ck}]
+
+ headers = [
+ {"X-OVH-Application", env(:app_key)},
+ {"X-OVH-Timestamp", ts},
+ {"X-OVH-Signature", "$1$" <> sign_hex},
+ {"X-Ovh-Consumer", ck}
+ ]
end
def parse_number(num) do
diff --git a/lib/plugins/tell.ex b/lib/plugins/tell.ex
index b4d05dc..923c2ef 100644
--- a/lib/plugins/tell.ex
+++ b/lib/plugins/tell.ex
@@ -8,6 +8,7 @@ defmodule Nola.Plugins.Tell do
"""
def irc_doc, do: @moduledoc
+
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@@ -24,7 +25,7 @@ defmodule Nola.Plugins.Tell do
regopts = [plugin: __MODULE__]
{:ok, _} = Registry.register(Nola.PubSub, "account", regopts)
{:ok, _} = Registry.register(Nola.PubSub, "trigger:tell", regopts)
- {:ok, dets} = :dets.open_file(dets(), [type: :bag])
+ {:ok, dets} = :dets.open_file(dets(), type: :bag)
{:ok, %{dets: dets}}
end
@@ -33,44 +34,59 @@ defmodule Nola.Plugins.Tell do
{:noreply, state}
end
- def handle_info({:irc, :trigger, "tell", m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: [target | message]}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "tell",
+ m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: [target | message]}}},
+ state
+ ) do
do_tell(state, m, target, message)
{: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 = Nola.Account.get(from)
- user = Nola.UserTrack.find_by_account(network, account)
- fromnick = if user, do: user.nick, else: account.name
- "#{nick}: <#{fromnick}> #{message}"
- end)
- Enum.each(strs, fn(s) -> Nola.Irc.Connection.broadcast_message(network, channel, s) end)
+ strs =
+ Enum.map(messages, fn {_, from, message, at} ->
+ account = Nola.Account.get(from)
+ user = Nola.UserTrack.find_by_account(network, account)
+ fromnick = if user, do: user.nick, else: account.name
+ "#{nick}: <#{fromnick}> #{message}"
+ end)
+
+ Enum.each(strs, fn s -> Nola.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.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
+ # :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.ets_mutate_select_each(:dets, state.dets, spec, fn table, obj ->
case obj do
- { {net, chan, ^old_id}, from_id, message, at } = obj ->
+ {{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
+
+ _ ->
+ :ok
end
end)
+
{:noreply, state}
end
-
def handle_info(info, state) do
{:noreply, state}
end
@@ -83,16 +99,15 @@ defmodule Nola.Plugins.Tell do
defp do_tell(state, m, nick_target, message) do
target = Nola.Account.find_always_by_nick(m.network, m.channel, nick_target)
message = Enum.join(message, " ")
- with \
- {:target, %Nola.Account{} = target} <- {:target, target},
+
+ with {:target, %Nola.Account{} = target} <- {:target, target},
{:same, false} <- {:same, target.id == m.account.id},
- target_user = Nola.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)),
+ target_user = Nola.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()}
+ {: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
@@ -102,5 +117,4 @@ defmodule Nola.Plugins.Tell do
{:message, _} -> m.replyfun.("can't tell without a message")
end
end
-
end
diff --git a/lib/plugins/txt.ex b/lib/plugins/txt.ex
index a66a984..b06e5ff 100644
--- a/lib/plugins/txt.ex
+++ b/lib/plugins/txt.ex
@@ -53,12 +53,21 @@ defmodule Nola.Plugins.Txt do
end
def init([]) do
- dets_locks_filename = (Nola.data_path() <> "/" <> "txtlocks.dets") |> String.to_charlist
+ dets_locks_filename = (Nola.data_path() <> "/" <> "txtlocks.dets") |> String.to_charlist()
{:ok, locks} = :dets.open_file(dets_locks_filename, [])
- markov_handler = Keyword.get(Application.get_env(:nola, __MODULE__, []), :markov_handler, Nola.Plugins.Txt.Markov.Native)
+
+ markov_handler =
+ Keyword.get(
+ Application.get_env(:nola, __MODULE__, []),
+ :markov_handler,
+ Nola.Plugins.Txt.Markov.Native
+ )
+
{:ok, markov} = markov_handler.start_link()
- {:ok, _} = Registry.register(Nola.PubSub, "triggers", [plugin: __MODULE__])
- {:ok, %__MODULE__{locks: locks, markov_handler: markov_handler, markov: markov, triggers: load()}}
+ {:ok, _} = Registry.register(Nola.PubSub, "triggers", plugin: __MODULE__)
+
+ {:ok,
+ %__MODULE__{locks: locks, markov_handler: markov_handler, markov: markov, triggers: load()}}
end
def handle_info({:received, "!reload", _, chan}, state) do
@@ -69,7 +78,10 @@ defmodule Nola.Plugins.Txt do
# ADMIN: RW/RO
#
- def handle_info({:irc, :trigger, "txtrw", msg = %{channel: channel, trigger: %{type: :plus}}}, state = %{rw: false}) do
+ def handle_info(
+ {:irc, :trigger, "txtrw", msg = %{channel: channel, trigger: %{type: :plus}}},
+ state = %{rw: false}
+ ) do
if channel && UserTrack.operator?(msg.network, channel, msg.sender.nick) do
msg.replyfun.("txt: écriture réactivée")
{:noreply, %__MODULE__{state | rw: true}}
@@ -78,7 +90,10 @@ defmodule Nola.Plugins.Txt do
end
end
- def handle_info({:irc, :trigger, "txtrw", msg = %{channel: channel, trigger: %{type: :minus}}}, state = %{rw: true}) do
+ def handle_info(
+ {:irc, :trigger, "txtrw", msg = %{channel: channel, trigger: %{type: :minus}}},
+ state = %{rw: true}
+ ) do
if channel && UserTrack.operator?(msg.network, channel, msg.sender.nick) do
msg.replyfun.("txt: écriture désactivée")
{:noreply, %__MODULE__{state | rw: false}}
@@ -91,26 +106,30 @@ defmodule Nola.Plugins.Txt do
# ADMIN: LOCKS
#
- def handle_info({:irc, :trigger, "txtlock", msg = %{trigger: %{type: :plus, args: [trigger]}}}, state) do
- with \
- {trigger, _} <- clean_trigger(trigger),
- true <- UserTrack.operator?(msg.network, msg.channel, msg.sender.nick)
- do
+ def handle_info(
+ {:irc, :trigger, "txtlock", msg = %{trigger: %{type: :plus, args: [trigger]}}},
+ state
+ ) do
+ with {trigger, _} <- clean_trigger(trigger),
+ true <- UserTrack.operator?(msg.network, msg.channel, msg.sender.nick) do
:dets.insert(state.locks, {trigger})
msg.replyfun.("txt: #{trigger} verrouillé")
end
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "txtlock", msg = %{trigger: %{type: :minus, args: [trigger]}}}, state) do
- with \
- {trigger, _} <- clean_trigger(trigger),
- true <- UserTrack.operator?(msg.network, msg.channel, msg.sender.nick),
- true <- :dets.member(state.locks, trigger)
- do
+ def handle_info(
+ {:irc, :trigger, "txtlock", msg = %{trigger: %{type: :minus, args: [trigger]}}},
+ state
+ ) do
+ with {trigger, _} <- clean_trigger(trigger),
+ true <- UserTrack.operator?(msg.network, msg.channel, msg.sender.nick),
+ true <- :dets.member(state.locks, trigger) do
:dets.delete(state.locks, trigger)
msg.replyfun.("txt: #{trigger} déverrouillé")
end
+
{:noreply, state}
end
@@ -119,26 +138,42 @@ defmodule Nola.Plugins.Txt do
#
def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :dot}}}, state) do
- map = Enum.map(state.triggers, fn({key, data}) ->
- ignore? = String.contains?(key, ".")
- locked? = case :dets.lookup(state.locks, key) do
- [{trigger}] -> "*"
- _ -> ""
- end
+ map =
+ Enum.map(state.triggers, fn {key, data} ->
+ ignore? = String.contains?(key, ".")
+
+ locked? =
+ case :dets.lookup(state.locks, key) do
+ [{trigger}] -> "*"
+ _ -> ""
+ end
+
+ unless ignore?, do: "#{key}: #{to_string(Enum.count(data))}#{locked?}"
+ end)
+ |> Enum.filter(& &1)
+
+ total =
+ Enum.reduce(state.triggers, 0, fn {_, data}, acc ->
+ acc + Enum.count(data)
+ end)
- unless ignore?, do: "#{key}: #{to_string(Enum.count(data))}#{locked?}"
- end)
- |> Enum.filter(& &1)
- total = Enum.reduce(state.triggers, 0, fn({_, data}, acc) ->
- acc + Enum.count(data)
- end)
detail = Enum.join(map, ", ")
- total = ". total: #{Enum.count(state.triggers)} fichiers, #{to_string(total)} lignes. Détail: https://sys.115ans.net/irc/txt"
+
+ link =
+ NolaWeb.Router.Helpers.irc_url(
+ NolaWeb.Endpoint,
+ :txt,
+ m.network,
+ NolaWeb.format_chan(m.channel)
+ )
+
+ total = "#{Enum.count(state.triggers)} fichiers, #{to_string(total)} lignes: #{link}"
ro = if !state.rw, do: " (lecture seule activée)", else: ""
- (detail<>total<>ro)
+ (detail <> total <> ro)
|> msg.replyfun.()
+
{:noreply, state}
end
@@ -147,48 +182,53 @@ defmodule Nola.Plugins.Txt do
#
def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :bang, args: []}}}, state) do
- result = Enum.reduce(state.triggers, [], fn({trigger, data}, acc) ->
- Enum.reduce(data, acc, fn({l, _}, acc) ->
- [{trigger, l} | acc]
+ result =
+ Enum.reduce(state.triggers, [], fn {trigger, data}, acc ->
+ Enum.reduce(data, acc, fn {l, _}, acc ->
+ [{trigger, l} | acc]
+ end)
end)
- end)
- |> Enum.shuffle()
+ |> Enum.shuffle()
if !Enum.empty?(result) do
{source, line} = Enum.random(result)
msg.replyfun.(format_line(line, "#{source}: ", msg))
end
+
{:noreply, state}
end
def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :bang, args: args}}}, state) do
- grep = Enum.join(args, " ")
- |> 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) ->
- if !String.contains?(trigger, ".") do
- Enum.reduce(data, acc, fn({l, _}, acc) ->
- [{trigger, l} | acc]
- end)
- else
- acc
- end
- end)
- |> Enum.filter(fn({_, line}) ->
- line
- |> String.downcase()
- |> :unicode.characters_to_nfd_binary()
- |> String.contains?(grep)
+ grep =
+ Enum.join(args, " ")
+ |> 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 ->
+ if !String.contains?(trigger, ".") do
+ Enum.reduce(data, acc, fn {l, _}, acc ->
+ [{trigger, l} | acc]
+ end)
+ else
+ acc
+ end
+ end)
+ |> Enum.filter(fn {_, line} ->
+ line
+ |> String.downcase()
+ |> :unicode.characters_to_nfd_binary()
+ |> String.contains?(grep)
+ end)
+ |> Enum.shuffle()
end)
- |> Enum.shuffle()
- end)
if result do
{source, line} = result
msg.replyfun.(["#{source}: " | line])
end
+
{:noreply, state}
end
@@ -200,11 +240,15 @@ defmodule Nola.Plugins.Txt do
end
def with_stateful_results(key, initfun) do
- pid = case :global.whereis_name(key) do
- :undefined ->
- start_stateful_results(key, initfun.())
- pid -> pid
- end
+ pid =
+ case :global.whereis_name(key) do
+ :undefined ->
+ start_stateful_results(key, initfun.())
+
+ pid ->
+ pid
+ end
+
if pid, do: wait_stateful_results(key, initfun, pid)
end
@@ -214,19 +258,24 @@ defmodule Nola.Plugins.Txt do
def start_stateful_results(key, list) do
me = self()
- {pid, _} = spawn_monitor(fn() ->
- Process.monitor(me)
- stateful_results(me, list)
- end)
+
+ {pid, _} =
+ spawn_monitor(fn ->
+ Process.monitor(me)
+ stateful_results(me, list)
+ end)
+
:yes = :global.register_name(key, pid)
pid
end
def wait_stateful_results(key, initfun, pid) do
send(pid, :get)
+
receive do
{:stateful_results, line} ->
line
+
{:DOWN, _ref, :process, ^pid, reason} ->
with_stateful_results(key, initfun)
after
@@ -246,10 +295,11 @@ defmodule Nola.Plugins.Txt do
:get ->
send(owner, {:stateful_results, line})
stateful_results(owner, rest)
+
{:DOWN, _ref, :process, ^owner, _} ->
:ok
- after
- @stateful_results_expire -> :ok
+ after
+ @stateful_results_expire -> :ok
end
end
@@ -261,20 +311,28 @@ defmodule Nola.Plugins.Txt do
case state.markov_handler.sentence(state.markov) do
{:ok, line} ->
msg.replyfun.(line)
+
error ->
- Logger.error "Txt Markov error: "<>inspect error
+ Logger.error("Txt Markov error: " <> inspect(error))
end
+
{:noreply, state}
end
- def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :tilde, args: complete}}}, state) do
+ def handle_info(
+ {:irc, :trigger, "txt", msg = %{trigger: %{type: :tilde, args: complete}}},
+ state
+ ) do
complete = Enum.join(complete, " ")
+
case state.markov_handler.complete_sentence(complete, state.markov) do
{:ok, line} ->
msg.replyfun.(line)
+
error ->
- Logger.error "Txt Markov error: "<>inspect error
+ Logger.error("Txt Markov error: " <> inspect(error))
end
+
{:noreply, state}
end
@@ -282,12 +340,13 @@ defmodule Nola.Plugins.Txt do
# TXT CREATE
#
- def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :plus, args: [trigger]}}}, state) do
- with \
- {trigger, _} <- clean_trigger(trigger),
- true <- can_write?(state, msg, trigger),
- :ok <- create_file(trigger)
- do
+ def handle_info(
+ {:irc, :trigger, "txt", msg = %{trigger: %{type: :plus, args: [trigger]}}},
+ state
+ ) do
+ with {trigger, _} <- clean_trigger(trigger),
+ true <- can_write?(state, msg, trigger),
+ :ok <- create_file(trigger) do
msg.replyfun.("#{trigger}.txt créé. Ajouter: `+#{trigger} …` ; Lire: `!#{trigger}`")
{:noreply, %__MODULE__{state | triggers: load()}}
else
@@ -301,23 +360,35 @@ defmodule Nola.Plugins.Txt do
def handle_info({:irc, :trigger, trigger, m = %{trigger: %{type: :query, args: opts}}}, state) do
{trigger, _} = clean_trigger(trigger)
+
if Map.get(state.triggers, trigger) do
- url = if m.channel do
- NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :txt, m.network, NolaWeb.format_chan(m.channel), trigger)
- else
- NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :txt, trigger)
- end
+ url =
+ if m.channel do
+ NolaWeb.Router.Helpers.irc_url(
+ NolaWeb.Endpoint,
+ :txt,
+ m.network,
+ NolaWeb.format_chan(m.channel),
+ trigger
+ )
+ else
+ NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :txt, trigger)
+ end
+
m.replyfun.("-> #{url}")
end
+
{:noreply, state}
end
def handle_info({:irc, :trigger, trigger, msg = %{trigger: %{type: :bang, args: opts}}}, state) do
{trigger, _} = clean_trigger(trigger)
line = get_random(msg, state.triggers, trigger, String.trim(Enum.join(opts, " ")))
+
if line do
msg.replyfun.(format_line(line, nil, msg))
end
+
{:noreply, state}
end
@@ -325,18 +396,20 @@ defmodule Nola.Plugins.Txt do
# TXT: ADD
#
- def handle_info({:irc, :trigger, trigger, msg = %{trigger: %{type: :plus, args: content}}}, state) do
- with \
- true <- can_write?(state, msg, trigger),
- {:ok, idx} <- add(state.triggers, msg.text)
- do
+ def handle_info(
+ {:irc, :trigger, trigger, msg = %{trigger: %{type: :plus, args: content}}},
+ state
+ ) do
+ with true <- can_write?(state, msg, trigger),
+ {:ok, idx} <- add(state.triggers, msg.text) do
msg.replyfun.("#{msg.sender.nick}: ajouté à #{trigger}. (#{idx})")
{:noreply, %__MODULE__{state | triggers: load()}}
else
{:error, {:jaro, string, idx}} ->
msg.replyfun.("#{msg.sender.nick}: doublon #{trigger}##{idx}: #{string}")
+
error ->
- Logger.debug("txt add failed: #{inspect error}")
+ Logger.debug("txt add failed: #{inspect(error)}")
{:noreply, state}
end
end
@@ -346,13 +419,11 @@ defmodule Nola.Plugins.Txt do
#
def handle_info({:irc, :trigger, trigger, msg = %{trigger: %{type: :minus, args: [id]}}}, state) do
- with \
- true <- can_write?(state, msg, trigger),
- data <- Map.get(state.triggers, trigger),
- {id, ""} <- Integer.parse(id),
- {text, _id} <- Enum.find(data, fn({_, idx}) -> id-1 == idx end)
- do
- data = data |> Enum.into(Map.new)
+ with true <- can_write?(state, msg, trigger),
+ data <- Map.get(state.triggers, trigger),
+ {id, ""} <- Integer.parse(id),
+ {text, _id} <- Enum.find(data, fn {_, idx} -> id - 1 == idx end) do
+ data = data |> Enum.into(Map.new())
data = Map.delete(data, text)
msg.replyfun.("#{msg.sender.nick}: #{trigger}.txt##{id} supprimée: #{text}")
dump(trigger, data)
@@ -363,7 +434,7 @@ defmodule Nola.Plugins.Txt do
end
end
- def handle_info(:reload_markov, state=%__MODULE__{triggers: triggers, markov: markov}) do
+ def handle_info(:reload_markov, state = %__MODULE__{triggers: triggers, markov: markov}) do
state.markov_handler.reload(state.triggers, state.markov)
{:noreply, state}
end
@@ -382,41 +453,48 @@ defmodule Nola.Plugins.Txt do
:dets.sync(state.locks)
:dets.close(state.locks)
end
+
:ok
end
# Load/Reloads text files from disk
defp load() do
- triggers = Path.wildcard(directory() <> "/*.txt")
- |> Enum.reduce(%{}, fn(path, m) ->
- file = Path.basename(path)
- key = String.replace(file, ".txt", "")
- data = directory() <> file
- |> File.read!
- |> String.split("\n")
- |> Enum.reject(fn(line) ->
- cond do
- line == "" -> true
- !line -> true
- true -> false
- end
+ triggers =
+ Path.wildcard(directory() <> "/*.txt")
+ |> Enum.reduce(%{}, fn path, m ->
+ file = Path.basename(path)
+ key = String.replace(file, ".txt", "")
+
+ data =
+ (directory() <> file)
+ |> File.read!()
+ |> String.split("\n")
+ |> Enum.reject(fn line ->
+ cond do
+ line == "" -> true
+ !line -> true
+ true -> false
+ end
+ end)
+ |> Enum.with_index()
+
+ Map.put(m, key, data)
end)
- |> Enum.with_index
- Map.put(m, key, data)
- end)
- |> Enum.sort
- |> Enum.into(Map.new)
+ |> Enum.sort()
+ |> Enum.into(Map.new())
send(self(), :reload_markov)
triggers
end
defp dump(trigger, data) do
- data = data
- |> Enum.sort_by(fn({_, idx}) -> idx end)
- |> Enum.map(fn({text, _}) -> text end)
- |> Enum.join("\n")
- File.write!(directory() <> "/" <> trigger <> ".txt", data<>"\n", [])
+ data =
+ data
+ |> Enum.sort_by(fn {_, idx} -> idx end)
+ |> Enum.map(fn {text, _} -> text end)
+ |> Enum.join("\n")
+
+ File.write!(directory() <> "/" <> trigger <> ".txt", data <> "\n", [])
end
defp get_random(msg, triggers, trigger, []) do
@@ -429,30 +507,36 @@ defmodule Nola.Plugins.Txt do
end
defp get_random(msg, triggers, trigger, opt) do
- arg = case Integer.parse(opt) do
- {pos, ""} -> {:index, pos}
- {_pos, _some_string} -> {:grep, opt}
- _error -> {:grep, opt}
- end
+ arg =
+ case Integer.parse(opt) do
+ {pos, ""} -> {:index, pos}
+ {_pos, _some_string} -> {:grep, opt}
+ _error -> {:grep, opt}
+ end
+
get_with_param(msg, triggers, trigger, arg)
end
defp get_with_param(msg, triggers, trigger, {:index, pos}) do
data = Map.get(triggers, trigger, %{})
- case Enum.find(data, fn({_, index}) -> index+1 == pos end) do
+
+ case Enum.find(data, fn {_, index} -> index + 1 == pos end) do
{text, _} -> text
_ -> nil
end
end
defp get_with_param(msg, triggers, trigger, {:grep, query}) do
- out = with_stateful_results(msg, {:grep, trigger, query}, fn() ->
- data = Map.get(triggers, trigger, %{})
- regex = Regex.compile!("#{query}", "i")
- Enum.filter(data, fn({txt, _}) -> Regex.match?(regex, txt) end)
- |> Enum.map(fn({txt, _}) -> txt end)
- |> Enum.shuffle()
- end)
+ out =
+ with_stateful_results(msg, {:grep, trigger, query}, fn ->
+ data = Map.get(triggers, trigger, %{})
+ regex = Regex.compile!("#{query}", "i")
+
+ Enum.filter(data, fn {txt, _} -> Regex.match?(regex, txt) end)
+ |> Enum.map(fn {txt, _} -> txt end)
+ |> Enum.shuffle()
+ end)
+
if out, do: out
end
@@ -466,50 +550,57 @@ defmodule Nola.Plugins.Txt do
[trigger, content] ->
{trigger, _} = clean_trigger(trigger)
-
if Map.has_key?(triggers, trigger) do
- jaro = Enum.find(triggers[trigger], fn({string, idx}) -> String.jaro_distance(content, string) > 0.9 end)
+ jaro =
+ Enum.find(triggers[trigger], fn {string, idx} ->
+ String.jaro_distance(content, string) > 0.9
+ end)
if jaro do
{string, idx} = jaro
- {:error, {:jaro, string, idx+1}}
+ {:error, {:jaro, string, idx + 1}}
else
- File.write!(directory() <> "/" <> trigger <> ".txt", content<>"\n", [:append])
- idx = Enum.count(triggers[trigger])+1
+ File.write!(directory() <> "/" <> trigger <> ".txt", content <> "\n", [:append])
+ idx = Enum.count(triggers[trigger]) + 1
{:ok, idx}
end
else
{:error, :notxt}
end
- _ -> {:error, :badarg}
+
+ _ ->
+ {:error, :badarg}
end
end
# fixme: this is definitely the ugliest thing i've ever done
defp clean_trigger(trigger) do
- [trigger | opts] = trigger
- |> String.strip
- |> String.split(" ", parts: 2)
-
- trigger = trigger
- |> String.downcase
- |> :unicode.characters_to_nfd_binary()
- |> String.replace(~r/[^a-z0-9._]/, "")
- |> String.trim(".")
- |> String.trim("_")
+ [trigger | opts] =
+ trigger
+ |> String.strip()
+ |> String.split(" ", parts: 2)
+
+ trigger =
+ trigger
+ |> String.downcase()
+ |> :unicode.characters_to_nfd_binary()
+ |> String.replace(~r/[^a-z0-9._]/, "")
+ |> String.trim(".")
+ |> String.trim("_")
{trigger, opts}
end
def format_line(line, prefix, msg) do
prefix = unless(prefix, do: "", else: prefix)
- prefix <> line
+
+ (prefix <> line)
|> String.split("\\\\")
- |> Enum.map(fn(line) ->
+ |> Enum.map(fn line ->
String.split(line, "\\\\\\\\")
end)
|> List.flatten()
- |> Enum.map(fn(line) ->
+ |> Enum.map(fn line ->
String.trim(line)
|> Tmpl.render(msg)
end)
@@ -521,10 +612,13 @@ defmodule Nola.Plugins.Txt do
defp can_write?(%{rw: rw?, locks: locks}, msg = %{channel: nil, sender: sender}, trigger) do
admin? = Nola.Irc.admin?(sender)
- locked? = case :dets.lookup(locks, trigger) do
- [{trigger}] -> true
- _ -> false
- end
+
+ locked? =
+ case :dets.lookup(locks, trigger) do
+ [{trigger}] -> true
+ _ -> false
+ end
+
unlocked? = if rw? == false, do: false, else: !locked?
can? = unlocked? || admin?
@@ -533,16 +627,24 @@ defmodule Nola.Plugins.Txt do
reason = if !rw?, do: "lecture seule", else: "fichier vérrouillé"
msg.replyfun.("#{sender.nick}: permission refusée (#{reason})")
end
+
can?
end
- defp can_write?(state = %__MODULE__{rw: rw?, locks: locks}, msg = %{channel: channel, sender: sender}, trigger) do
+ defp can_write?(
+ state = %__MODULE__{rw: rw?, locks: locks},
+ msg = %{channel: channel, sender: sender},
+ trigger
+ ) do
admin? = Nola.Irc.admin?(sender)
operator? = Nola.UserTrack.operator?(msg.network, channel, sender.nick)
- locked? = case :dets.lookup(locks, trigger) do
- [{trigger}] -> true
- _ -> false
- end
+
+ locked? =
+ case :dets.lookup(locks, trigger) do
+ [{trigger}] -> true
+ _ -> false
+ end
+
unlocked? = if rw? == false, do: false, else: !locked?
can? = admin? || operator? || unlocked?
@@ -550,7 +652,7 @@ defmodule Nola.Plugins.Txt do
reason = if !rw?, do: "lecture seule", else: "fichier vérrouillé"
msg.replyfun.("#{sender.nick}: permission refusée (#{reason})")
end
+
can?
end
-
end
diff --git a/lib/plugins/txt/markov.ex b/lib/plugins/txt/markov.ex
index b47666c..2b3d210 100644
--- a/lib/plugins/txt/markov.ex
+++ b/lib/plugins/txt/markov.ex
@@ -1,9 +1,7 @@
defmodule Nola.Plugins.Txt.Markov do
-
@type state :: any()
@callback start_link() :: {:ok, state()}
- @callback reload(content :: Map.t, state()) :: any()
- @callback sentence(state()) :: {:ok, String.t} | {:error, String.t}
- @callback complete_sentence(state()) :: {:ok, String.t} | {:error, String.t}
-
+ @callback reload(content :: Map.t(), state()) :: any()
+ @callback sentence(state()) :: {:ok, String.t()} | {:error, String.t()}
+ @callback complete_sentence(state()) :: {:ok, String.t()} | {:error, String.t()}
end
diff --git a/lib/plugins/txt/markov_py_markovify.ex b/lib/plugins/txt/markov_py_markovify.ex
index f79ed47..47ff0a7 100644
--- a/lib/plugins/txt/markov_py_markovify.ex
+++ b/lib/plugins/txt/markov_py_markovify.ex
@@ -1,5 +1,4 @@
defmodule Nola.Plugins.Txt.MarkovPyMarkovify do
-
def start_link() do
{:ok, nil}
end
@@ -19,7 +18,8 @@ defmodule Nola.Plugins.Txt.MarkovPyMarkovify do
defp run(args \\ []) do
{binary, script} = script()
args = [script, Path.expand(Nola.Plugins.Txt.directory()) | args]
- IO.puts "Args #{inspect args}"
+ IO.puts("Args #{inspect(args)}")
+
case MuonTrap.cmd(binary, args) do
{response, 0} -> response
{response, code} -> "error #{code}: #{response}"
@@ -28,12 +28,11 @@ defmodule Nola.Plugins.Txt.MarkovPyMarkovify do
defp script() do
default_script = to_string(:code.priv_dir(:nola)) <> "/irc/txt/markovify.py"
- env = Application.get_env(:nola, Nola.Plugins.Txt, [])
- |> Keyword.get(:py_markovify, [])
+
+ env =
+ Application.get_env(:nola, Nola.Plugins.Txt, [])
+ |> Keyword.get(:py_markovify, [])
{Keyword.get(env, :python, "python3"), Keyword.get(env, :script, default_script)}
end
-
-
-
end
diff --git a/lib/plugins/untappd.ex b/lib/plugins/untappd.ex
index e409172..5a4c070 100644
--- a/lib/plugins/untappd.ex
+++ b/lib/plugins/untappd.ex
@@ -1,5 +1,4 @@
defmodule Nola.Plugins.Untappd do
-
def irc_doc() do
"""
# [Untappd](https://untappd.com)
@@ -18,49 +17,67 @@ defmodule Nola.Plugins.Untappd do
end
def init(_) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:beer", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:beer", plugin: __MODULE__)
{:ok, %{}}
end
- def handle_info({:irc, :trigger, _, m = %Nola.Message{trigger: %{type: :bang, args: args}}}, state) do
+ def handle_info(
+ {:irc, :trigger, _, m = %Nola.Message{trigger: %{type: :bang, args: args}}},
+ state
+ ) do
case Untappd.search_beer(Enum.join(args, " "), limit: 1) do
{:ok, %{"response" => %{"beers" => %{"count" => count, "items" => [result | _]}}}} ->
%{"beer" => beer, "brewery" => brewery} = result
- description = Map.get(beer, "beer_description")
- |> String.replace("\n", " ")
- |> String.replace("\r", " ")
- |> String.trim()
- beer_s = "#{Map.get(brewery, "brewery_name")}: #{Map.get(beer, "beer_name")} - #{Map.get(beer, "beer_abv")}°"
+
+ description =
+ Map.get(beer, "beer_description")
+ |> String.replace("\n", " ")
+ |> String.replace("\r", " ")
+ |> String.trim()
+
+ beer_s =
+ "#{Map.get(brewery, "brewery_name")}: #{Map.get(beer, "beer_name")} - #{Map.get(beer, "beer_abv")}°"
+
city = get_in(brewery, ["location", "brewery_city"])
- location = [Map.get(brewery, "brewery_type"), city, Map.get(brewery, "country_name")]
- |> Enum.filter(fn(x) -> x end)
- |> Enum.join(", ")
+
+ location =
+ [Map.get(brewery, "brewery_type"), city, Map.get(brewery, "country_name")]
+ |> Enum.filter(fn x -> x end)
+ |> Enum.join(", ")
+
extra = "#{Map.get(beer, "beer_style")} - IBU: #{Map.get(beer, "beer_ibu")} - #{location}"
m.replyfun.([beer_s, extra, description])
+
err ->
m.replyfun.("Error")
end
+
{:noreply, state}
end
-
- def handle_info({:irc, :trigger, _, m = %Nola.Message{trigger: %{type: :query, args: args}}}, state) do
+ def handle_info(
+ {:irc, :trigger, _, m = %Nola.Message{trigger: %{type: :query, args: args}}},
+ state
+ ) do
case Untappd.search_beer(Enum.join(args, " ")) do
{:ok, %{"response" => %{"beers" => %{"count" => count, "items" => results}}}} ->
- beers = for %{"beer" => beer, "brewery" => brewery} <- results do
- "#{Map.get(brewery, "brewery_name")}: #{Map.get(beer, "beer_name")} - #{Map.get(beer, "beer_abv")}°"
- end
- |> Enum.intersperse(", ")
- |> Enum.join("")
+ beers =
+ for %{"beer" => beer, "brewery" => brewery} <- results do
+ "#{Map.get(brewery, "brewery_name")}: #{Map.get(beer, "beer_name")} - #{Map.get(beer, "beer_abv")}°"
+ end
+ |> Enum.intersperse(", ")
+ |> Enum.join("")
+
m.replyfun.("#{count}. #{beers}")
+
err ->
m.replyfun.("Error")
end
+
{:noreply, state}
end
def handle_info(info, state) do
{:noreply, state}
end
-
end
diff --git a/lib/plugins/user_mention.ex b/lib/plugins/user_mention.ex
index 634167e..1a9881c 100644
--- a/lib/plugins/user_mention.ex
+++ b/lib/plugins/user_mention.ex
@@ -19,11 +19,24 @@ defmodule Nola.Plugins.UserMention do
{:ok, nil}
end
- def handle_info({:irc, :trigger, nick, message = %Nola.Message{sender: sender, account: account, network: network, channel: channel, trigger: %Nola.Trigger{type: :at, args: content}}}, state) do
- nick = nick
- |> String.trim(":")
- |> String.trim(",")
+ def handle_info(
+ {:irc, :trigger, nick,
+ message = %Nola.Message{
+ sender: sender,
+ account: account,
+ network: network,
+ channel: channel,
+ trigger: %Nola.Trigger{type: :at, args: content}
+ }},
+ state
+ ) do
+ nick =
+ nick
+ |> String.trim(":")
+ |> String.trim(",")
+
target = Nola.Account.find_always_by_nick(network, channel, nick)
+
if target do
telegram = Nola.Account.get_meta(target, "telegram-id")
sms = Nola.Account.get_meta(target, "sms-number")
@@ -31,22 +44,27 @@ defmodule Nola.Plugins.UserMention do
cond do
telegram ->
- Nola.Telegram.send_message(telegram, "`#{channel}` <**#{sender.nick}**> #{Enum.join(content, " ")}")
+ Nola.Telegram.send_message(
+ telegram,
+ "`#{channel}` <**#{sender.nick}**> #{Enum.join(content, " ")}"
+ )
+
sms ->
case Nola.Plugins.Sms.send_sms(sms, text) do
{:error, code} -> message.replyfun("#{sender.nick}: erreur #{code} (sms)")
end
+
true ->
Nola.Plugins.Tell.tell(message, nick, content)
end
else
false
end
+
{:noreply, state}
end
def handle_info(_, state) do
{:noreply, state}
end
-
end
diff --git a/lib/plugins/wikipedia.ex b/lib/plugins/wikipedia.ex
index 47b14da..0bcbee7 100644
--- a/lib/plugins/wikipedia.ex
+++ b/lib/plugins/wikipedia.ex
@@ -15,15 +15,24 @@ defmodule Nola.Plugins.Wikipedia do
end
def init(_) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:wp", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:wp", plugin: __MODULE__)
{:ok, nil}
end
- def handle_info({:irc, :trigger, _, message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: []}}}, state) do
+ def handle_info(
+ {:irc, :trigger, _,
+ message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: []}}},
+ state
+ ) do
irc_random(message)
{:noreply, state}
end
- def handle_info({:irc, :trigger, _, message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}}, state) do
+
+ def handle_info(
+ {:irc, :trigger, _,
+ message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}},
+ state
+ ) do
irc_search(Enum.join(args, " "), message)
{:noreply, state}
end
@@ -33,19 +42,22 @@ defmodule Nola.Plugins.Wikipedia do
end
defp irc_search("", message), do: irc_random(message)
+
defp irc_search(query, message) do
params = %{
"action" => "query",
"list" => "search",
"srsearch" => String.strip(query),
- "srlimit" => 1,
+ "srlimit" => 1
}
+
case query_wikipedia(params) do
{:ok, %{"query" => %{"search" => [item | _]}}} ->
title = item["title"]
url = "https://fr.wikipedia.org/wiki/" <> String.replace(title, " ", "_")
msg = "Wikipédia: #{title} — #{url}"
message.replyfun.(msg)
+
_ ->
nil
end
@@ -58,6 +70,7 @@ defmodule Nola.Plugins.Wikipedia do
"grnnamespace" => 0,
"prop" => "info"
}
+
case query_wikipedia(params) do
{:ok, %{"query" => %{"pages" => map = %{}}}} ->
[{_, item}] = Map.to_list(map)
@@ -65,6 +78,7 @@ defmodule Nola.Plugins.Wikipedia do
url = "https://fr.wikipedia.org/wiki/" <> String.replace(title, " ", "_")
msg = "Wikipédia: #{title} — #{url}"
message.replyfun.(msg)
+
_ ->
nil
end
@@ -72,19 +86,23 @@ defmodule Nola.Plugins.Wikipedia do
defp query_wikipedia(params) do
url = "https://fr.wikipedia.org/w/api.php"
- params = params
- |> Map.put("format", "json")
- |> Map.put("utf8", "")
+
+ params =
+ params
+ |> Map.put("format", "json")
+ |> Map.put("utf8", "")
case HTTPoison.get(url, [], params: params) do
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> Jason.decode(body)
+ {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
+ Jason.decode(body)
+
{:ok, %HTTPoison.Response{status_code: 400, body: body}} ->
- Logger.error "Wikipedia HTTP 400: #{inspect body}"
+ Logger.error("Wikipedia HTTP 400: #{inspect(body)}")
{:error, "http 400"}
+
error ->
- Logger.error "Wikipedia http error: #{inspect error}"
+ Logger.error("Wikipedia http error: #{inspect(error)}")
{:error, "http client error"}
end
end
-
end
diff --git a/lib/plugins/wolfram_alpha.ex b/lib/plugins/wolfram_alpha.ex
index 120af16..f9d5a5e 100644
--- a/lib/plugins/wolfram_alpha.ex
+++ b/lib/plugins/wolfram_alpha.ex
@@ -15,33 +15,44 @@ defmodule Nola.Plugins.WolframAlpha do
end
def init(_) do
- {:ok, _} = Registry.register(Nola.PubSub, "trigger:wa", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(Nola.PubSub, "trigger:wa", plugin: __MODULE__)
{:ok, nil}
end
- def handle_info({:irc, :trigger, _, m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: query}}}, state) do
+ def handle_info(
+ {:irc, :trigger, _, m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: query}}},
+ state
+ ) do
query = Enum.join(query, " ")
+
params = %{
"appid" => Keyword.get(Application.get_env(:nola, :wolframalpha, []), :app_id, "NO_APP_ID"),
"units" => "metric",
"i" => query
}
+
url = "https://www.wolframalpha.com/input/?i=" <> URI.encode(query)
- case HTTPoison.get("http://api.wolframalpha.com/v1/result", [], [params: params]) do
+
+ case HTTPoison.get("http://api.wolframalpha.com/v1/result", [], params: params) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
m.replyfun.(["#{query} -> #{body}", url])
+
{:ok, %HTTPoison.Response{status_code: code, body: body}} ->
- error = case {code, body} do
- {501, b} -> "input invalide: #{body}"
- {code, error} -> "erreur #{code}: #{body || ""}"
- end
+ error =
+ case {code, body} do
+ {501, b} -> "input invalide: #{body}"
+ {code, error} -> "erreur #{code}: #{body || ""}"
+ end
+
m.replyfun.("wa: #{error}")
+
{:error, %HTTPoison.Error{reason: reason}} ->
m.replyfun.("wa: erreur http: #{to_string(reason)}")
+
_ ->
m.replyfun.("wa: erreur http")
end
+
{:noreply, state}
end
-
end
diff --git a/lib/plugins/youtube.ex b/lib/plugins/youtube.ex
index 39bf03d..5e36301 100644
--- a/lib/plugins/youtube.ex
+++ b/lib/plugins/youtube.ex
@@ -16,11 +16,17 @@ defmodule Nola.Plugins.YouTube do
end
def init([]) do
- for t <- ["trigger:yt", "trigger:youtube"], do: {:ok, _} = Registry.register(Nola.PubSub, t, [plugin: __MODULE__])
+ for t <- ["trigger:yt", "trigger:youtube"],
+ do: {:ok, _} = Registry.register(Nola.PubSub, t, plugin: __MODULE__)
+
{:ok, %__MODULE__{}}
end
- def handle_info({:irc, :trigger, _, message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}}, state) do
+ def handle_info(
+ {:irc, :trigger, _,
+ message = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: args}}},
+ state
+ ) do
irc_search(Enum.join(args, " "), message)
{:noreply, state}
end
@@ -34,71 +40,91 @@ defmodule Nola.Plugins.YouTube do
{:ok, %{"items" => [item | _]}} ->
url = "https://youtube.com/watch?v=" <> item["id"]
snippet = item["snippet"]
- duration = item["contentDetails"]["duration"] |> String.replace("PT", "") |> String.downcase
- date = snippet["publishedAt"]
- |> DateTime.from_iso8601()
- |> elem(1)
- |> Timex.format("{relative}", :relative)
- |> elem(1)
-
- info_line = "— #{duration} — uploaded by #{snippet["channelTitle"]} — #{date}"
- <> " — #{item["statistics"]["viewCount"]} views, #{item["statistics"]["likeCount"]} likes,"
- <> " #{item["statistics"]["dislikeCount"]} dislikes"
+
+ duration =
+ item["contentDetails"]["duration"] |> String.replace("PT", "") |> String.downcase()
+
+ date =
+ snippet["publishedAt"]
+ |> DateTime.from_iso8601()
+ |> elem(1)
+ |> Timex.format("{relative}", :relative)
+ |> elem(1)
+
+ info_line =
+ "— #{duration} — uploaded by #{snippet["channelTitle"]} — #{date}" <>
+ " — #{item["statistics"]["viewCount"]} views, #{item["statistics"]["likeCount"]} likes," <>
+ " #{item["statistics"]["dislikeCount"]} dislikes"
+
message.replyfun.("#{snippet["title"]} — #{url}")
message.replyfun.(info_line)
+
{:error, error} ->
- message.replyfun.("Erreur YouTube: "<>error)
+ message.replyfun.("Erreur YouTube: " <> error)
+
_ ->
nil
end
end
defp search(query) do
- query = query
- |> String.strip
+ query =
+ query
+ |> String.strip()
+
key = Application.get_env(:nola, :youtube)[:api_key]
+
params = %{
"key" => key,
"maxResults" => 1,
"part" => "id",
"safeSearch" => "none",
"type" => "video",
- "q" => query,
+ "q" => query
}
+
url = "https://www.googleapis.com/youtube/v3/search"
+
case HTTPoison.get(url, [], params: params) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, json} = Jason.decode(body)
item = List.first(json["items"])
+
if item do
video_id = item["id"]["videoId"]
+
params = %{
"part" => "snippet,contentDetails,statistics",
"id" => video_id,
"key" => key
}
+
headers = []
options = [params: params]
+
case HTTPoison.get("https://www.googleapis.com/youtube/v3/videos", [], options) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
Jason.decode(body)
+
{:ok, %HTTPoison.Response{status_code: code, body: body}} ->
- Logger.error "YouTube HTTP #{code}: #{inspect body}"
+ Logger.error("YouTube HTTP #{code}: #{inspect(body)}")
{:error, "http #{code}"}
+
error ->
- Logger.error "YouTube http error: #{inspect error}"
+ Logger.error("YouTube http error: #{inspect(error)}")
:error
end
else
:error
end
+
{:ok, %HTTPoison.Response{status_code: code, body: body}} ->
- Logger.error "YouTube HTTP #{code}: #{inspect body}"
+ Logger.error("YouTube HTTP #{code}: #{inspect(body)}")
{:error, "http #{code}"}
+
error ->
- Logger.error "YouTube http error: #{inspect error}"
+ Logger.error("YouTube http error: #{inspect(error)}")
:error
end
end
-
end
diff --git a/lib/telegram.ex b/lib/telegram.ex
index b161b63..289b913 100644
--- a/lib/telegram.ex
+++ b/lib/telegram.ex
@@ -34,42 +34,62 @@ defmodule Nola.Telegram do
{:ok, %{room_state: room_state}}
end
- def handle_update(%{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/start"<>_}}, _token, state) do
- text = "*Welcome to beautte!*\n\nQuery the bot on IRC and say \"enable-telegram\" to continue."
+ def handle_update(
+ %{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/start" <> _}},
+ _token,
+ state
+ ) do
+ text =
+ "*Welcome to beautte!*\n\nQuery the bot on IRC and say \"enable-telegram\" to continue."
+
send_message(m["chat"]["id"], text)
{:ok, %{account: nil}}
end
- def handle_update(%{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/enable"<>_}}, _token, state) do
- key = case String.split(text, " ") do
- ["/enable", key | _] -> key
- _ -> "nil"
- end
+ def handle_update(
+ %{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/enable" <> _}},
+ _token,
+ state
+ ) do
+ key =
+ case String.split(text, " ") do
+ ["/enable", key | _] -> key
+ _ -> "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"}],
# "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"},
# "message_id" => 11, "text" => "/enable salope"}, "update_id" => 764148578}
account = Nola.Account.find_meta_account("telegram-validation-code", String.downcase(key))
- text = if account do
- net = Nola.Account.get_meta(account, "telegram-validation-target")
- Nola.Account.put_meta(account, "telegram-id", m["chat"]["id"])
- Nola.Account.put_meta(account, "telegram-username", m["chat"]["username"])
- Nola.Account.put_meta(account, "telegram-username", m["chat"]["username"])
- Nola.Account.delete_meta(account, "telegram-validation-code")
- Nola.Account.delete_meta(account, "telegram-validation-target")
- Nola.Irc.Connection.broadcast_message(net, account, "Telegram #{m["chat"]["username"]} account added!")
- "Yay! Linked to account **#{account.name}**."
- else
- "Token invalid"
- end
+
+ text =
+ if account do
+ net = Nola.Account.get_meta(account, "telegram-validation-target")
+ Nola.Account.put_meta(account, "telegram-id", m["chat"]["id"])
+ Nola.Account.put_meta(account, "telegram-username", m["chat"]["username"])
+ Nola.Account.put_meta(account, "telegram-username", m["chat"]["username"])
+ Nola.Account.delete_meta(account, "telegram-validation-code")
+ Nola.Account.delete_meta(account, "telegram-validation-target")
+
+ Nola.Irc.Connection.broadcast_message(
+ net,
+ account,
+ "Telegram #{m["chat"]["username"]} account added!"
+ )
+
+ "Yay! Linked to account **#{account.name}**."
+ else
+ "Token invalid"
+ end
+
send_message(m["chat"]["id"], text)
{:ok, %{account: account.id}}
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"},
@@ -87,7 +107,7 @@ defmodule Nola.Telegram do
end
end
- #[debug] Unhandled update: %{"callback_query" =>
+ # [debug] Unhandled update: %{"callback_query" =>
# %{
# "chat_instance" => "-7948978714441865930", "data" => "evolu.net/#dmz",
# "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"},
@@ -101,79 +121,139 @@ defmodule Nola.Telegram do
# }
# , "update_id" => 218161568}
- #def handle_update(t, %{"callback_query" => cb = %{"data" => "resend", "id" => id, "message" => m = %{"message_id" => m_id, "chat" => %{"id" => chat_id}, "reply_to_message" => op}}}) do
- #end
+ # def handle_update(t, %{"callback_query" => cb = %{"data" => "resend", "id" => id, "message" => m = %{"message_id" => m_id, "chat" => %{"id" => chat_id}, "reply_to_message" => op}}}) do
+ # end
- def handle_update(%{"callback_query" => cb = %{"data" => "start-upload:"<>target, "id" => id, "message" => m = %{"message_id" => m_id, "chat" => %{"id" => chat_id}, "reply_to_message" => op}}}, t, state) do
+ def handle_update(
+ %{
+ "callback_query" =>
+ cb = %{
+ "data" => "start-upload:" <> target,
+ "id" => id,
+ "message" =>
+ m = %{
+ "message_id" => m_id,
+ "chat" => %{"id" => chat_id},
+ "reply_to_message" => op
+ }
+ }
+ },
+ t,
+ state
+ ) do
account = Nola.Account.find_meta_account("telegram-id", chat_id)
+
if account do
- target = case String.split(target, "/") do
- ["everywhere"] -> Nola.Membership.of_account(account)
- [net, chan] -> [{net, chan}]
- end
- Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "Processing...", reply_markup: %{})
-
- {content, type} = cond do
- op["photo"] -> {op["photo"], ""}
- op["voice"] -> {op["voice"], " a voice message"}
- op["video"] -> {op["video"], ""}
- op["document"] -> {op["document"], ""}
- op["animation"] -> {op["animation"], ""}
- end
+ target =
+ case String.split(target, "/") do
+ ["everywhere"] -> Nola.Membership.of_account(account)
+ [net, chan] -> [{net, chan}]
+ end
+
+ Telegram.Api.request(t, "editMessageText",
+ chat_id: chat_id,
+ message_id: m_id,
+ text: "Processing...",
+ reply_markup: %{}
+ )
+
+ {content, type} =
+ cond do
+ op["photo"] -> {op["photo"], ""}
+ op["voice"] -> {op["voice"], " a voice message"}
+ op["video"] -> {op["video"], ""}
+ op["document"] -> {op["document"], ""}
+ op["animation"] -> {op["animation"], ""}
+ end
+
+ file =
+ if is_list(content) && Enum.count(content) > 1 do
+ Enum.sort_by(content, fn p -> p["file_size"] end, &>=/2)
+ |> List.first()
+ else
+ content
+ end
- file = if is_list(content) && Enum.count(content) > 1 do
- Enum.sort_by(content, fn(p) -> p["file_size"] end, &>=/2)
- |> List.first()
- else
- content
- end
file_id = file["file_id"]
file_unique_id = file["file_unique_id"]
- text = if(op["caption"], do: ": "<> op["caption"] <> "", else: "")
- resend = %{"inline_keyboard" => [ [%{"text" => "re-share", "callback_data" => "resend"}] ]}
- spawn(fn() ->
- with \
- {:ok, file} <- Telegram.Api.request(t, "getFile", file_id: file_id),
- path = "https://api.telegram.org/file/bot#{t}/#{file["file_path"]}",
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path),
- <<smol_body::binary-size(20), _::binary>> = body,
- {:ok, magic} <- GenMagic.Pool.perform(Nola.GenMagic, {:bytes, smol_body}),
- bucket = Application.get_env(:nola, :s3, []) |> Keyword.get(:bucket),
- ext = Path.extname(file["file_path"]),
- s3path = "#{account.id}/#{file_unique_id}#{ext}",
- Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "*Uploading...*", reply_markup: %{}, parse_mode: "MarkdownV2"),
- s3req = ExAws.S3.put_object(bucket, s3path, body, acl: :public_read, content_type: magic.mime_type),
- {:ok, _} <- ExAws.request(s3req)
- do
+ text = if(op["caption"], do: ": " <> op["caption"] <> "", else: "")
+ resend = %{"inline_keyboard" => [[%{"text" => "re-share", "callback_data" => "resend"}]]}
+
+ spawn(fn ->
+ with {:ok, file} <- Telegram.Api.request(t, "getFile", file_id: file_id),
+ path = "https://api.telegram.org/file/bot#{t}/#{file["file_path"]}",
+ {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path),
+ <<smol_body::binary-size(20), _::binary>> = body,
+ {:ok, magic} <- GenMagic.Pool.perform(Nola.GenMagic, {:bytes, smol_body}),
+ bucket = Application.get_env(:nola, :s3, []) |> Keyword.get(:bucket),
+ ext = Path.extname(file["file_path"]),
+ s3path = "#{account.id}/#{file_unique_id}#{ext}",
+ Telegram.Api.request(t, "editMessageText",
+ chat_id: chat_id,
+ message_id: m_id,
+ text: "*Uploading...*",
+ reply_markup: %{},
+ parse_mode: "MarkdownV2"
+ ),
+ s3req =
+ ExAws.S3.put_object(bucket, s3path, body,
+ acl: :public_read,
+ content_type: magic.mime_type
+ ),
+ {:ok, _} <- ExAws.request(s3req) do
path = NolaWeb.Router.Helpers.url(NolaWeb.Endpoint) <> "/files/#{s3path}"
- sent = for {net, chan} <- target do
- txt = "sent#{type}#{text} #{path}"
- Nola.Irc.send_message_as(account, net, chan, txt)
- "#{net}/#{chan}"
- end
+
+ sent =
+ for {net, chan} <- target do
+ txt = "sent#{type}#{text} #{path}"
+ Nola.Irc.send_message_as(account, net, chan, txt)
+ "#{net}/#{chan}"
+ end
+
if caption = op["caption"], do: as_irc_message(chat_id, caption, account)
text = "Sent on " <> Enum.join(sent, ", ") <> " !"
- Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "_Sent!_", reply_markup: %{}, parse_mode: "MarkdownV2")
+
+ Telegram.Api.request(t, "editMessageText",
+ chat_id: chat_id,
+ message_id: m_id,
+ text: "_Sent!_",
+ reply_markup: %{},
+ parse_mode: "MarkdownV2"
+ )
else
error ->
- Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "Something failed.", reply_markup: %{}, parse_mode: "MarkdownV2")
- Logger.error("Failed upload from Telegram: #{inspect error}")
+ Telegram.Api.request(t, "editMessageText",
+ chat_id: chat_id,
+ message_id: m_id,
+ text: "Something failed.",
+ reply_markup: %{},
+ parse_mode: "MarkdownV2"
+ )
+
+ Logger.error("Failed upload from Telegram: #{inspect(error)}")
end
end)
end
+
{:ok, state}
end
- def handle_update(%{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}, "text" => text}}, _, state) do
+ def handle_update(
+ %{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}, "text" => text}},
+ _,
+ state
+ ) do
account = Nola.Account.find_meta_account("telegram-id", id)
+
if account do
as_irc_message(id, text, account)
end
+
{:ok, state}
end
def handle_update(m, _, state) do
- Logger.debug("Unhandled update: #{inspect m}")
+ Logger.debug("Unhandled update: #{inspect(m)}")
{:ok, state}
end
@@ -188,49 +268,80 @@ defmodule Nola.Telegram do
end
defp as_irc_message(id, text, account) do
- reply_fun = fn(text) -> send_message(id, text) end
- trigger_text = cond do
- String.starts_with?(text, "/") ->
- "/"<>text = text
- "!"<>text
- Enum.any?(Nola.Irc.Connection.triggers(), fn({trigger, _}) -> String.starts_with?(text, trigger) end) ->
- text
- true ->
- "!"<>text
- end
- message = %Nola.Message{
- id: FlakeId.get(),
- transport: :telegram,
+ reply_fun = fn text -> send_message(id, text) end
+
+ trigger_text =
+ cond do
+ String.starts_with?(text, "/") ->
+ "/" <> text = text
+ "!" <> text
+
+ Enum.any?(Nola.Irc.Connection.triggers(), fn {trigger, _} ->
+ String.starts_with?(text, trigger)
+ end) ->
+ text
+
+ true ->
+ "!" <> text
+ end
+
+ message = %Nola.Message{
+ id: FlakeId.get(),
+ transport: :telegram,
network: "telegram",
channel: nil,
text: text,
account: account,
sender: %ExIRC.SenderInfo{nick: account.name},
replyfun: reply_fun,
- trigger: Nola.Irc.Connection.extract_trigger(trigger_text),
- at: nil
+ trigger: Nola.Irc.Connection.extract_trigger(trigger_text),
+ at: nil
}
- Nola.Irc.Connection.publish(message, ["messages:private", "messages:telegram", "telegram/#{account.id}:messages"])
+
+ Nola.Irc.Connection.publish(message, [
+ "messages:private",
+ "messages:telegram",
+ "telegram/#{account.id}:messages"
+ ])
+
message
end
- defp start_upload(_type, %{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}}}, token, state) do
+ defp start_upload(
+ _type,
+ %{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}}},
+ token,
+ state
+ ) do
account = Nola.Account.find_meta_account("telegram-id", id)
+
if account do
text = if(m["text"], do: m["text"], else: nil)
- targets = Nola.Membership.of_account(account)
- |> Enum.map(fn({net, chan}) -> "#{net}/#{chan}" end)
- |> Enum.map(fn(i) -> %{"text" => i, "callback_data" => "start-upload:#{i}"} end)
- kb = if Enum.count(targets) > 1 do
- [%{"text" => "everywhere", "callback_data" => "start-upload:everywhere"}] ++ targets
- else
- targets
- end
- |> Enum.chunk_every(2)
+
+ targets =
+ Nola.Membership.of_account(account)
+ |> Enum.map(fn {net, chan} -> "#{net}/#{chan}" end)
+ |> Enum.map(fn i -> %{"text" => i, "callback_data" => "start-upload:#{i}"} end)
+
+ kb =
+ if Enum.count(targets) > 1 do
+ [%{"text" => "everywhere", "callback_data" => "start-upload:everywhere"}] ++ targets
+ else
+ targets
+ end
+ |> Enum.chunk_every(2)
+
keyboard = %{"inline_keyboard" => kb}
- Telegram.Api.request(token, "sendMessage", chat_id: id, text: "Where should I send this file?", reply_markup: keyboard, reply_to_message_id: m["message_id"], parse_mode: "MarkdownV2")
+
+ Telegram.Api.request(token, "sendMessage",
+ chat_id: id,
+ text: "Where should I send this file?",
+ reply_markup: keyboard,
+ reply_to_message_id: m["message_id"],
+ parse_mode: "MarkdownV2"
+ )
end
+
{:ok, state}
end
-
end
diff --git a/lib/telegram/room.ex b/lib/telegram/room.ex
index 9db551b..05c3eeb 100644
--- a/lib/telegram/room.ex
+++ b/lib/telegram/room.ex
@@ -7,7 +7,7 @@ defmodule Nola.TelegramRoom do
def rooms(), do: rooms(:with_docs)
- @spec rooms(:with_docs | :ids) :: [Map.t | integer( )]
+ @spec rooms(:with_docs | :ids) :: [Map.t() | integer()]
def rooms(:with_docs) do
case Couch.get(@couch, :all_docs, include_docs: true) do
{:ok, %{"rows" => rows}} -> {:ok, for(%{"doc" => doc} <- rows, do: doc)}
@@ -33,9 +33,13 @@ defmodule Nola.TelegramRoom do
def after_start() do
{:ok, rooms} = rooms(:ids)
+
for id <- rooms do
- spawn(fn() ->
- Telegram.Bot.ChatBot.Chat.Session.Supervisor.start_child(Nola.Telegram, Integer.parse(id) |> elem(0))
+ spawn(fn ->
+ Telegram.Bot.ChatBot.Chat.Session.Supervisor.start_child(
+ Nola.Telegram,
+ Integer.parse(id) |> elem(0)
+ )
end)
end
end
@@ -45,34 +49,50 @@ defmodule Nola.TelegramRoom do
token = Keyword.get(Application.get_env(:nola, :telegram, []), :key)
{:ok, chat} = Api.request(token, "getChat", chat_id: id)
Logger.metadata(transport: :telegram, id: id, telegram_room_id: id)
- tg_room = case room(to_string(id)) do
- {:ok, tg_room = %{"network" => _net, "channel" => _chan}} -> tg_room
- _ ->
- [net, chan] = String.split(chat["title"], "/", parts: 2)
- {net, chan} = case Nola.Irc.Connection.get_network(net, chan) do
- %Nola.Irc.Connection{} -> {net, chan}
- _ -> {nil, nil}
- end
- {:ok, _, _} = Couch.post(@couch, %{"_id" => id, "network" => net, "channel" => chan})
- {:ok, tg_room} = room(to_string(id))
- tg_room
- end
+
+ tg_room =
+ case room(to_string(id)) do
+ {:ok, tg_room = %{"network" => _net, "channel" => _chan}} ->
+ tg_room
+
+ _ ->
+ [net, chan] = String.split(chat["title"], "/", parts: 2)
+
+ {net, chan} =
+ case Nola.Irc.Connection.get_network(net, chan) do
+ %Nola.Irc.Connection{} -> {net, chan}
+ _ -> {nil, nil}
+ end
+
+ {:ok, _, _} = Couch.post(@couch, %{"_id" => id, "network" => net, "channel" => chan})
+ {:ok, tg_room} = room(to_string(id))
+ tg_room
+ end
+
%{"network" => net, "channel" => chan} = tg_room
- Logger.info("Starting ChatBot for room #{id} \"#{chat["title"]}\" #{inspect tg_room}")
- irc_plumbed = if net && chan do
+ Logger.info("Starting ChatBot for room #{id} \"#{chat["title"]}\" #{inspect(tg_room)}")
+
+ irc_plumbed =
+ if net && chan do
{:ok, _} = Registry.register(Nola.PubSub, "#{net}/#{chan}:messages", plugin: __MODULE__)
{:ok, _} = Registry.register(Nola.PubSub, "#{net}/#{chan}:triggers", plugin: __MODULE__)
{:ok, _} = Registry.register(Nola.PubSub, "#{net}/#{chan}:outputs", plugin: __MODULE__)
true
- else
- Logger.warn("Did not found telegram match for #{id} \"#{chat["title"]}\"")
- false
- end
+ else
+ Logger.warn("Did not found telegram match for #{id} \"#{chat["title"]}\"")
+ false
+ end
+
{:ok, %{id: id, net: net, chan: chan, irc: irc_plumbed}}
end
def init(id) do
- Logger.error("telegram_room: bad id (not room id)", transport: :telegram, id: id, telegram_room_id: id)
+ Logger.error("telegram_room: bad id (not room id)",
+ transport: :telegram,
+ id: id,
+ telegram_room_id: id
+ )
+
:ignore
end
@@ -82,15 +102,18 @@ defmodule Nola.TelegramRoom do
else
first_name = Map.get(from, "first_name")
last_name = Map.get(from, "last_name")
- name = [first_name, last_name]
- |> Enum.filter(& &1)
- |> Enum.join(" ")
+
+ name =
+ [first_name, last_name]
+ |> Enum.filter(& &1)
+ |> Enum.join(" ")
username = Map.get(from, "username", first_name)
- account = username
- |> Nola.Account.new_account()
- |> Nola.Account.update_account_name(name)
+ account =
+ username
+ |> Nola.Account.new_account()
+ |> Nola.Account.update_account_name(name)
Nola.Account.put_meta(account, "telegram-id", user_id)
@@ -99,17 +122,33 @@ defmodule Nola.TelegramRoom do
end
end
- def handle_update(%{"message" => %{"from" => from = %{"id" => user_id}, "text" => text}}, _token, state) do
+ def handle_update(
+ %{"message" => %{"from" => from = %{"id" => user_id}, "text" => text}},
+ _token,
+ state
+ ) do
account = find_or_create_meta_account(from, state)
- #connection = Nola.Irc.Connection.get_network(state.net)
+ # connection = Nola.Irc.Connection.get_network(state.net)
Nola.Irc.send_message_as(account, state.net, state.chan, text, true, origin: __MODULE__)
{:ok, state}
end
- def handle_update(data = %{"message" => %{"from" => from = %{"id" => user_id}, "location" => %{"latitude" => lat, "longitude" => lon}}}, _token, state) do
+ def handle_update(
+ data = %{
+ "message" => %{
+ "from" => from = %{"id" => user_id},
+ "location" => %{"latitude" => lat, "longitude" => lon}
+ }
+ },
+ _token,
+ state
+ ) do
account = find_or_create_meta_account(from, state)
- #connection = Nola.Irc.Connection.get_network(state.net)
- Nola.Irc.send_message_as(account, state.net, state.chan, "@ #{lat}, #{lon}", true, origin: __MODULE__)
+ # connection = Nola.Irc.Connection.get_network(state.net)
+ Nola.Irc.send_message_as(account, state.net, state.chan, "@ #{lat}, #{lon}", true,
+ origin: __MODULE__
+ )
+
{:ok, state}
end
@@ -133,62 +172,76 @@ defmodule Nola.TelegramRoom do
body = if Map.get(message.meta, :self), do: text, else: "<#{nick}> #{text}"
Nola.Telegram.send_message(state.id, body)
end
+
{:ok, state}
end
def handle_info(info, state) do
- Logger.info("UNhandled #{inspect info}")
+ Logger.info("UNhandled #{inspect(info)}")
{:ok, state}
end
- defp upload(_type, %{"message" => m = %{"chat" => %{"id" => chat_id}, "from" => from = %{"id" => user_id}}}, token, state) do
+ defp upload(
+ _type,
+ %{"message" => m = %{"chat" => %{"id" => chat_id}, "from" => from = %{"id" => user_id}}},
+ token,
+ state
+ ) do
account = find_or_create_meta_account(from, state)
+
if account do
- {content, type} = cond do
- m["photo"] -> {m["photo"], "photo"}
- m["voice"] -> {m["voice"], "voice message"}
- m["video"] -> {m["video"], "video"}
- m["document"] -> {m["document"], "file"}
- m["animation"] -> {m["animation"], "gif"}
- end
+ {content, type} =
+ cond do
+ m["photo"] -> {m["photo"], "photo"}
+ m["voice"] -> {m["voice"], "voice message"}
+ m["video"] -> {m["video"], "video"}
+ m["document"] -> {m["document"], "file"}
+ m["animation"] -> {m["animation"], "gif"}
+ end
- file = if is_list(content) && Enum.count(content) > 1 do
- Enum.sort_by(content, fn(p) -> p["file_size"] end, &>=/2)
- |> List.first()
- else
- content
- end
+ file =
+ if is_list(content) && Enum.count(content) > 1 do
+ Enum.sort_by(content, fn p -> p["file_size"] end, &>=/2)
+ |> List.first()
+ else
+ content
+ end
file_id = file["file_id"]
file_unique_id = file["file_unique_id"]
text = if(m["caption"], do: m["caption"] <> " ", else: "")
- spawn(fn() ->
- with \
- {:ok, file} <- Telegram.Api.request(token, "getFile", file_id: file_id),
- path = "https://api.telegram.org/file/bot#{token}/#{file["file_path"]}",
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path),
- <<smol_body::binary-size(20), _::binary>> = body,
- {:ok, magic} <- GenMagic.Pool.perform(Nola.GenMagic, {:bytes, smol_body}),
- bucket = Application.get_env(:nola, :s3, []) |> Keyword.get(:bucket),
- ext = Path.extname(file["file_path"]),
- s3path = "#{account.id}/#{file_unique_id}#{ext}",
- s3req = ExAws.S3.put_object(bucket, s3path, body, acl: :public_read, content_type: magic.mime_type),
- {:ok, _} <- ExAws.request(s3req)
- do
+ spawn(fn ->
+ with {:ok, file} <- Telegram.Api.request(token, "getFile", file_id: file_id),
+ path = "https://api.telegram.org/file/bot#{token}/#{file["file_path"]}",
+ {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path),
+ <<smol_body::binary-size(20), _::binary>> = body,
+ {:ok, magic} <- GenMagic.Pool.perform(Nola.GenMagic, {:bytes, smol_body}),
+ bucket = Application.get_env(:nola, :s3, []) |> Keyword.get(:bucket),
+ ext = Path.extname(file["file_path"]),
+ s3path = "#{account.id}/#{file_unique_id}#{ext}",
+ s3req =
+ ExAws.S3.put_object(bucket, s3path, body,
+ acl: :public_read,
+ content_type: magic.mime_type
+ ),
+ {:ok, _} <- ExAws.request(s3req) do
path = NolaWeb.Router.Helpers.url(NolaWeb.Endpoint) <> "/files/#{s3path}"
txt = "#{type}: #{text}#{path}"
- #connection = Nola.Irc.Connection.get_network(state.net)
+ # connection = Nola.Irc.Connection.get_network(state.net)
Nola.Irc.send_message_as(account, state.net, state.chan, txt, true, origin: __MODULE__)
else
error ->
- Telegram.Api.request(token, "sendMessage", chat_id: chat_id, text: "File upload failed, sorry.")
- Logger.error("Failed upload from Telegram: #{inspect error}")
+ Telegram.Api.request(token, "sendMessage",
+ chat_id: chat_id,
+ text: "File upload failed, sorry."
+ )
+
+ Logger.error("Failed upload from Telegram: #{inspect(error)}")
end
end)
{:ok, state}
end
end
-
end
diff --git a/lib/tmpl.ex b/lib/tmpl.ex
index e4489ac..881cbc8 100644
--- a/lib/tmpl.ex
+++ b/lib/tmpl.ex
@@ -24,10 +24,26 @@ defmodule Tmpl do
end
end
- @colors [:white, :black, :blue, :green, :red, :brown, :purple, :orange, :yellow, :light_green, :cyan, :light_blue, :pink, :grey, :light_grey]
+ @colors [
+ :white,
+ :black,
+ :blue,
+ :green,
+ :red,
+ :brown,
+ :purple,
+ :orange,
+ :yellow,
+ :light_green,
+ :cyan,
+ :light_blue,
+ :pink,
+ :grey,
+ :light_grey
+ ]
for {color, index} <- Enum.with_index(@colors) do
- code = 48+index
+ code = 48 + index
def color_code(unquote(color)) do
unquote(code)
@@ -42,7 +58,9 @@ defmodule Tmpl do
end
end
- def account_nick(%{"id" => id, "name" => name}, %{variables: %{"message" => %{"network" => network}}}) do
+ def account_nick(%{"id" => id, "name" => name}, %{
+ variables: %{"message" => %{"network" => network}}
+ }) do
if user = Nola.UserTrack.find_by_account(network, %Nola.Account{id: id}) do
user.nick
else
@@ -53,7 +71,6 @@ defmodule Tmpl do
def account_nick(val, ctx) do
"{{account_nick}}"
end
-
end
def render(template, msg = %Nola.Message{}, context \\ %{}, safe \\ true) do
@@ -64,20 +81,23 @@ defmodule Tmpl do
case Liquex.parse(template) 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}]"
+ Logger.debug("Liquid error: #{pos} - #{inspect(template)}")
+ "[liquid ast error (at #{pos}): #{inspect(err)}]"
end
end
defp do_render(template_ast, context, safe) when is_list(template_ast) do
- context = Liquex.Context.new(mapify(context, safe))
- |> Map.put(:filter_module, Tmpl.Filter)
+ context =
+ Liquex.Context.new(mapify(context, safe))
+ |> Map.put(:filter_module, Tmpl.Filter)
+
{content, _context} = Liquex.render(template_ast, context)
to_string(content)
rescue
e ->
- Logger.error("Liquid error: #{inspect e}")
+ Logger.error("Liquid error: #{inspect(e)}")
"[liquid rendering error]"
end
@@ -87,8 +107,9 @@ defmodule Tmpl do
defp mapify(map = %{}, safe) do
map
- |> Enum.reduce(Map.new, fn({k,v}, acc) ->
+ |> Enum.reduce(Map.new(), fn {k, v}, acc ->
k = to_string(k)
+
if safe?(k, safe) do
if v = mapify(v, safe) do
Map.put(acc, k, v)
@@ -121,4 +142,3 @@ defmodule Tmpl do
defp safe?("password", true), do: false
defp safe?(_, true), do: true
end
-
diff --git a/lib/untappd.ex b/lib/untappd.ex
index e603c25..c563fa9 100644
--- a/lib/untappd.ex
+++ b/lib/untappd.ex
@@ -1,12 +1,12 @@
defmodule Untappd do
-
- @env Mix.env
- @version Mix.Project.config[:version]
+ @env Mix.env()
+ @version Mix.Project.config()[:version]
require Logger
def auth_url() do
client_id = Keyword.get(env(), :client_id)
url = NolaWeb.Router.Helpers.untappd_callback_url(NolaWeb.Endpoint, :callback)
+
"https://untappd.com/oauth/authenticate/?client_id=#{client_id}&response_type=code&redirect_url=#{URI.encode(url)}"
end
@@ -14,6 +14,7 @@ defmodule Untappd do
client_id = Keyword.get(env(), :client_id)
client_secret = Keyword.get(env(), :client_secret)
url = NolaWeb.Router.Helpers.untappd_callback_url(NolaWeb.Endpoint, :callback)
+
params = %{
"client_id" => client_id,
"client_secret" => client_secret,
@@ -21,12 +22,14 @@ defmodule Untappd do
"redirect_url" => url,
"code" => code
}
+
case HTTPoison.get("https://untappd.com/oauth/authorize", headers(), params: params) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
json = Poison.decode!(body)
{:ok, get_in(json, ["response", "access_token"])}
+
error ->
- Logger.error("Untappd auth callback failed: #{inspect error}")
+ Logger.error("Untappd auth callback failed: #{inspect(error)}")
:error
end
end
@@ -40,38 +43,56 @@ defmodule Untappd do
end
def checkin(token, beer_id) do
- params = get_params(token: token)
- |> Map.put("timezone", "CEST")
- |> Map.put("bid", beer_id)
- form_params = params
- |> Enum.into([])
- case HTTPoison.post("https://api.untappd.com/v4/checkin/add", {:form, form_params}, headers(), params: params) do
+ params =
+ get_params(token: token)
+ |> Map.put("timezone", "CEST")
+ |> Map.put("bid", beer_id)
+
+ form_params =
+ params
+ |> Enum.into([])
+
+ case HTTPoison.post("https://api.untappd.com/v4/checkin/add", {:form, form_params}, headers(),
+ params: params
+ ) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
- body = Jason.decode!(body)
- |> Map.get("response")
+ body =
+ Jason.decode!(body)
+ |> Map.get("response")
+
{:ok, body}
+
{:ok, resp = %HTTPoison.Response{status_code: code, body: body}} ->
- Logger.warn "Untappd checkin error: #{inspect resp}"
+ Logger.warn("Untappd checkin error: #{inspect(resp)}")
{:error, {:http_error, code}}
- {:error, error} -> {:error, {:http_error, error}}
+
+ {:error, error} ->
+ {:error, {:http_error, error}}
end
end
def search_beer(query, params \\ []) do
- params = get_params(params)
- |> Map.put("q", query)
- |> Map.put("limit", 10)
- #|> Map.put("sort", "name")
+ params =
+ get_params(params)
+ |> Map.put("q", query)
+ |> Map.put("limit", 10)
+
+ # |> Map.put("sort", "name")
case HTTPoison.get("https://api.untappd.com/v4/search/beer", headers(), params: params) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, Jason.decode!(body)}
+
error ->
- Logger.error("Untappd search error: #{inspect error}")
+ Logger.error("Untappd search error: #{inspect(error)}")
end
end
def get_params(params) do
- auth = %{"client_id" => Keyword.get(env(), :client_id), "client_secret" => Keyword.get(env(), :client_secret)}
+ auth = %{
+ "client_id" => Keyword.get(env(), :client_id),
+ "client_secret" => Keyword.get(env(), :client_secret)
+ }
+
if token = Keyword.get(params, :token) do
Map.put(auth, "access_token", token)
else
@@ -81,14 +102,14 @@ defmodule Untappd do
def headers(extra \\ []) do
client_id = Keyword.get(env(), :client_id)
- extra
- ++ [
- {"user-agent", "dmzbot (#{client_id}; #{@version}-#{@env})"}
- ]
+
+ extra ++
+ [
+ {"user-agent", "dmzbot (#{client_id}; #{@version}-#{@env})"}
+ ]
end
def env() do
Application.get_env(:nola, :untappd)
end
-
end
diff --git a/lib/util.ex b/lib/util.ex
index a402519..71fddab 100644
--- a/lib/util.ex
+++ b/lib/util.ex
@@ -1,7 +1,5 @@
defmodule Util do
-
defmodule Map do
-
def put_if_not_null(map, _key, nil) do
map
end
@@ -9,11 +7,11 @@ defmodule Util do
def put_if_not_null(map, key, value) do
Elixir.Map.put(map, key, value)
end
-
end
def to_naive_date_time(naive = %NaiveDateTime{}), do: naive
def to_naive_date_time(datetime = %DateTime{}), do: DateTime.to_naive(datetime)
+
def to_naive_date_time(timestamp) when is_integer(timestamp) do
timestamp
|> to_date_time()
@@ -42,7 +40,8 @@ defmodule Util do
def plusminus(number) when number < 0, do: "#{number}"
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(int) when is_integer(int), do: {int + 0.0, ""}
+
def float_paparse(string) when is_binary(string) do
string
|> String.replace(",", ".")
@@ -57,7 +56,7 @@ defmodule Util do
ets.safe_fixtable(table, false)
end
- defp do_ets_mutate_select_each(_, _, _, :'$end_of_table') do
+ defp do_ets_mutate_select_each(_, _, _, :"$end_of_table") do
:ok
end
@@ -66,7 +65,6 @@ defmodule Util do
do_ets_mutate_select_each(ets, table, fun, ets.select(continuation))
end
-
def ets_mutate_each(ets, table, fun) do
ets.safe_fixtable(table, true)
first = ets.first(table)
@@ -80,7 +78,7 @@ defmodule Util do
[elem] -> fun.(table, elem)
_ -> nil
end
+
do_ets_mutate_each(ets, table, fun, ets.next(table, key))
end
-
end
diff --git a/lib/web.ex b/lib/web.ex
index c1720a0..e8cb26d 100644
--- a/lib/web.ex
+++ b/lib/web.ex
@@ -25,22 +25,24 @@ defmodule NolaWeb do
"♯"
end
- def format_chan("#"<>chan) do
+ def format_chan("#" <> chan) do
chan
end
- def format_chan(chan = "!"<>_), do: chan
+ def format_chan(chan = "!" <> _), do: chan
def reformat_chan("♯") do
"#"
end
+
def reformat_chan("♯♯") do
"##"
end
- def reformat_chan(chan = "!"<>_), do: chan
+
+ def reformat_chan(chan = "!" <> _), do: chan
def reformat_chan(chan) do
- "#"<>chan
+ "#" <> chan
end
def controller do
@@ -55,8 +57,9 @@ defmodule NolaWeb do
def view do
quote do
- use Phoenix.View, root: "lib/web/templates",
- namespace: NolaWeb
+ use Phoenix.View,
+ root: "lib/web/templates",
+ namespace: NolaWeb
# Import convenience functions from controllers
import Phoenix.Controller, only: [get_flash: 2, view_module: 1]
diff --git a/lib/web/channels/user_socket.ex b/lib/web/channels/user_socket.ex
index eadd4e0..a910ffe 100644
--- a/lib/web/channels/user_socket.ex
+++ b/lib/web/channels/user_socket.ex
@@ -5,7 +5,7 @@ defmodule NolaWeb.UserSocket do
# channel "room:*", NolaWeb.RoomChannel
## Transports
- #transport :websocket, Phoenix.Transports.WebSocket
+ # transport :websocket, Phoenix.Transports.WebSocket
# transport :longpoll, Phoenix.Transports.LongPoll
# Socket params are passed from the client and can
diff --git a/lib/web/components/component.ex b/lib/web/components/component.ex
index fff8263..5894536 100644
--- a/lib/web/components/component.ex
+++ b/lib/web/components/component.ex
@@ -9,6 +9,7 @@ defmodule NolaWeb.Component do
def naive_date_time_utc(assigns = %{format: format}) do
assigns = assign(assigns, :format, Map.get(@date_time_formats, format, format))
+
~H"""
<time class="component"
id={"time-#{:erlang.phash2(@datetime)}"}
@@ -19,9 +20,11 @@ defmodule NolaWeb.Component do
</time>
"""
end
+
def naive_date_time_utc(assigns) do
naive_date_time_utc(assign(assigns, :format, "%F %H:%M"))
end
+
def get_luxon_format("%H:%M:%S"), do: "TIME_24_WITH_SECONDS"
def nick(assigns = %{self: false}) do
@@ -39,6 +42,4 @@ defmodule NolaWeb.Component do
</span>
"""
end
-
-
end
diff --git a/lib/web/components/event_component.ex b/lib/web/components/event_component.ex
index 8af3c67..32af856 100644
--- a/lib/web/components/event_component.ex
+++ b/lib/web/components/event_component.ex
@@ -38,6 +38,4 @@ defmodule NolaWeb.EventComponent do
joined
"""
end
-
-
end
diff --git a/lib/web/components/message_component.ex b/lib/web/components/message_component.ex
index 5d0386b..7f9bac7 100644
--- a/lib/web/components/message_component.ex
+++ b/lib/web/components/message_component.ex
@@ -8,5 +8,4 @@ defmodule NolaWeb.MessageComponent do
<div class="inline-block flex-grow cursor-default"><%= @text %></div>
"""
end
-
end
diff --git a/lib/web/context_plug.ex b/lib/web/context_plug.ex
index 0a16340..aca0431 100644
--- a/lib/web/context_plug.ex
+++ b/lib/web/context_plug.ex
@@ -8,21 +8,27 @@ defmodule NolaWeb.ContextPlug do
def get_account(conn) do
cond do
- get_session(conn, :account) -> get_session(conn, :account)
- get_session(conn, :oidc_id) -> if account = Nola.Account.find_meta_account("identity-id", get_session(conn, :oidc_id)), do: account.id
- true -> nil
+ get_session(conn, :account) ->
+ get_session(conn, :account)
+
+ get_session(conn, :oidc_id) ->
+ if account = Nola.Account.find_meta_account("identity-id", get_session(conn, :oidc_id)),
+ do: account.id
+
+ true ->
+ nil
end
end
def call(conn, opts) do
- account = with \
- {:account, account_id} when is_binary(account_id) <- {:account, get_account(conn)},
- {:account, account} when not is_nil(account) <- {:account, Nola.Account.get(account_id)}
- do
- account
- else
- _ -> nil
- end
+ account =
+ with {:account, account_id} when is_binary(account_id) <- {:account, get_account(conn)},
+ {:account, account} when not is_nil(account) <-
+ {:account, Nola.Account.get(account_id)} do
+ account
+ else
+ _ -> nil
+ end
network = Map.get(conn.params, "network")
network = if network == "-", do: nil, else: network
@@ -30,32 +36,46 @@ defmodule NolaWeb.ContextPlug do
oidc_account = Nola.Account.find_meta_account("identity-id", get_session(conn, :oidc_id))
conns = Nola.Irc.Connection.get_network(network)
- chan = if c = Map.get(conn.params, "chan") do
- NolaWeb.reformat_chan(c)
- end
+
+ chan =
+ if c = Map.get(conn.params, "chan") do
+ NolaWeb.reformat_chan(c)
+ end
+
chan_conn = Nola.Irc.Connection.get_network(network, chan)
- memberships = if account do
- Nola.Membership.of_account(account)
- end
+ memberships =
+ if account do
+ Nola.Membership.of_account(account)
+ end
- auth_required = cond do
- Keyword.get(opts, :restrict) == :public -> false
- account == nil -> true
- network == nil -> false
- Keyword.get(opts, :restrict) == :logged_in -> false
- network && chan ->
- !Enum.member?(memberships, {network, chan})
- network ->
- !Enum.any?(memberships, fn({n, _}) -> n == network end)
- end
+ auth_required =
+ cond do
+ Keyword.get(opts, :restrict) == :public ->
+ false
- bot = cond do
- network && chan && chan_conn -> chan_conn.nick
- network && conns -> conns.nick
- true -> nil
- end
+ account == nil ->
+ true
+ network == nil ->
+ false
+
+ Keyword.get(opts, :restrict) == :logged_in ->
+ false
+
+ network && chan ->
+ !Enum.member?(memberships, {network, chan})
+
+ network ->
+ !Enum.any?(memberships, fn {n, _} -> n == network end)
+ end
+
+ bot =
+ cond do
+ network && chan && chan_conn -> chan_conn.nick
+ network && conns -> conns.nick
+ true -> nil
+ end
cond do
account && auth_required ->
@@ -63,30 +83,34 @@ defmodule NolaWeb.ContextPlug do
|> put_status(404)
|> text("Page not found")
|> halt()
+
auth_required ->
conn
|> put_status(403)
|> render(NolaWeb.AlcoologView, "auth.html", bot: bot, no_header: true, network: network)
|> halt()
- (network && !conns) ->
+
+ network && !conns ->
conn
|> put_status(404)
|> text("Page not found")
|> halt()
- (chan && !chan_conn) ->
+
+ chan && !chan_conn ->
conn
|> put_status(404)
|> text("Page not found")
|> halt()
+
true ->
- conn = conn
- |> assign(:network, network)
- |> assign(:chan, chan)
- |> assign(:bot, bot)
- |> assign(:account, account)
- |> assign(:oidc_account, oidc_account)
- |> assign(:memberships, memberships)
+ conn =
+ conn
+ |> assign(:network, network)
+ |> assign(:chan, chan)
+ |> assign(:bot, bot)
+ |> assign(:account, account)
+ |> assign(:oidc_account, oidc_account)
+ |> assign(:memberships, memberships)
end
end
-
end
diff --git a/lib/web/controllers/alcoolog_controller.ex b/lib/web/controllers/alcoolog_controller.ex
index 8d7fc11..57fc16a 100644
--- a/lib/web/controllers/alcoolog_controller.ex
+++ b/lib/web/controllers/alcoolog_controller.ex
@@ -2,43 +2,63 @@ defmodule NolaWeb.AlcoologController do
use NolaWeb, :controller
require Logger
- plug NolaWeb.ContextPlug when action not in [:token]
- plug NolaWeb.ContextPlug, [restrict: :public] when action in [:token]
+ plug(NolaWeb.ContextPlug when action not in [:token])
+ plug(NolaWeb.ContextPlug, [restrict: :public] when action in [:token])
def token(conn, %{"token" => token}) do
case Nola.Token.lookup(token) do
- {:ok, {:alcoolog, :index, network, channel}} -> index(conn, nil, network, channel)
+ {:ok, {:alcoolog, :index, network, channel}} ->
+ index(conn, nil, network, channel)
+
err ->
- Logger.debug("AlcoologControler: token #{inspect err} invalid")
+ Logger.debug("AlcoologControler: token #{inspect(err)} invalid")
+
conn
|> put_status(404)
|> text("Page not found")
end
end
- def nick(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ def nick(
+ conn = %{assigns: %{account: account}},
+ params = %{"network" => network, "nick" => nick}
+ ) do
profile_account = Nola.Account.find_always_by_nick(network, nick, nick)
days = String.to_integer(Map.get(params, "days", "180"))
friend? = Enum.member?(Nola.Membership.friends(account), profile_account.id)
+
if friend? do
stats = Nola.Plugins.Alcoolog.get_full_statistics(profile_account.id)
- history = for {{nick, ts}, points, active, cl, deg, type, descr, meta} <- Nola.Plugins.Alcoolog.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()
+
+ history =
+ for {{nick, ts}, points, active, cl, deg, type, descr, meta} <-
+ Nola.Plugins.Alcoolog.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)
+ |> render("user.html",
+ network: network,
+ profile: profile_account,
+ days: days,
+ nick: nick,
+ history: history,
+ stats: stats
+ )
else
conn
|> put_status(404)
@@ -46,9 +66,13 @@ defmodule NolaWeb.AlcoologController do
end
end
- def nick_stats_json(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ def nick_stats_json(
+ conn = %{assigns: %{account: account}},
+ params = %{"network" => network, "nick" => nick}
+ ) do
profile_account = Nola.Account.find_always_by_nick(network, nick, nick)
friend? = Enum.member?(Nola.Membership.friends(account), profile_account.id)
+
if friend? do
stats = Nola.Plugins.Alcoolog.get_full_statistics(profile_account.id)
@@ -62,27 +86,39 @@ defmodule NolaWeb.AlcoologController do
end
end
- def nick_gls_json(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ def nick_gls_json(
+ conn = %{assigns: %{account: account}},
+ params = %{"network" => network, "nick" => nick}
+ ) do
profile_account = Nola.Account.find_always_by_nick(network, nick, nick)
friend? = Enum.member?(Nola.Membership.friends(account), profile_account.id)
count = String.to_integer(Map.get(params, "days", "180"))
+
if friend? do
data = Nola.Plugins.Alcoolog.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))
+ 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")
@@ -94,29 +130,39 @@ defmodule NolaWeb.AlcoologController do
end
end
-
-
- def nick_volumes_json(conn = %{assigns: %{account: account}}, params = %{"network" => network, "nick" => nick}) do
+ def nick_volumes_json(
+ conn = %{assigns: %{account: account}},
+ params = %{"network" => network, "nick" => nick}
+ ) do
profile_account = Nola.Account.find_always_by_nick(network, nick, nick)
friend? = Enum.member?(Nola.Membership.friends(account), profile_account.id)
count = String.to_integer(Map.get(params, "days", "180"))
+
if friend? do
data = Nola.Plugins.Alcoolog.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))
+ 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")
@@ -128,22 +174,29 @@ defmodule NolaWeb.AlcoologController do
end
end
- def nick_log_json(conn = %{assigns: %{account: account}}, %{"network" => network, "nick" => nick}) do
+ def nick_log_json(conn = %{assigns: %{account: account}}, %{
+ "network" => network,
+ "nick" => nick
+ }) do
profile_account = Nola.Account.find_always_by_nick(network, nick, nick)
friend? = Enum.member?(Nola.Membership.friends(account), profile_account.id)
+
if friend? do
- history = for {{nick, ts}, points, active, cl, deg, type, descr, meta} <- Nola.Plugins.Alcoolog.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
+ history =
+ for {{nick, ts}, points, active, cl, deg, type, descr, meta} <-
+ Nola.Plugins.Alcoolog.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} = Nola.Plugins.Alcoolog.user_stats(profile_account)
last = %{last | active: active, at: DateTime.utc_now() |> DateTime.to_iso8601()}
@@ -159,13 +212,19 @@ defmodule NolaWeb.AlcoologController do
end
end
- def nick_history_json(conn = %{assigns: %{account: account}}, %{"network" => network, "nick" => nick}) do
+ def nick_history_json(conn = %{assigns: %{account: account}}, %{
+ "network" => network,
+ "nick" => nick
+ }) do
profile_account = Nola.Account.find_always_by_nick(network, nick, nick)
friend? = Enum.member?(Nola.Membership.friends(account), profile_account.id)
+
if friend? do
- history = for {_, date, value} <- Nola.Plugins.AlcoologAnnouncer.log(profile_account) do
- %{date: DateTime.to_iso8601(date), value: value}
- end
+ history =
+ for {_, date, value} <- Nola.Plugins.AlcoologAnnouncer.log(profile_account) do
+ %{date: DateTime.to_iso8601(date), value: value}
+ end
+
conn
|> put_resp_content_type("application/json")
|> text(Jason.encode!(history))
@@ -184,7 +243,7 @@ defmodule NolaWeb.AlcoologController do
index(conn, account, nil, nil)
end
- #def index(conn, params) do
+ # def index(conn, params) do
# network = Map.get(params, "network")
# chan = if c = Map.get(params, "chan") do
# NolaWeb.reformat_chan(c)
@@ -197,117 +256,175 @@ defmodule NolaWeb.AlcoologController do
# conn
# |> put_status(403)
# |> render("auth.html", network: network, channel: chan, irc_conn: conn, bot: bot)
- #end
+ # end
def index(conn, account, network, channel) do
- aday = ((24 * 60)*60)
+ aday = 24 * 60 * 60
now = DateTime.utc_now()
- 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)
+
+ 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", {:const, before15}},
- ], [:"$_"]}
+ {{{:_, :"$1"}, :_, :_, :_, :_, :_, :_, :_},
+ [
+ {:>, :"$1", {:const, before15}}
+ ], [:"$_"]}
]
- # tuple ets: {{nick, date}, volumes, current, nom, commentaire}
+ # tuple ets: {{nick, date}, volumes, current, nom, commentaire}
members = Nola.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)
- drinks = :ets.select(Nola.Plugins.Alcoolog.ETS, match)
- |> 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)
+ 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(Nola.Plugins.Alcoolog.ETS, match)
+ |> 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 = Nola.Plugins.Alcoolog.get_channel_statistics(account, network, channel)
- 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)
- end)
- |> Enum.sort_by(fn({_nick, count}) -> count end, &>/2)
+ 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)
+ end)
+ |> Enum.sort_by(fn {_nick, count} -> count end, &>/2)
+
# {date, single_peak}
#
conn
|> assign(:title, "alcoolog")
- |> render("index.html", network: network, channel: channel, drinks: drinks, top: top, stats: stats)
+ |> 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
+ def index_gls_json(conn = %{assigns: %{account: account}}, %{
+ "network" => network,
+ "chan" => channel
+ }) do
count = 30
channel = NolaWeb.reformat_chan(channel)
members = Nola.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)
+ 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(Nola.Plugins.Alcoolog.user_over_time_gl(account, count), gls, fn({date, gl}, gls) ->
- u = Map.get(gls, date, %{})
+
+ 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(Nola.Plugins.Alcoolog.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)
+
+ Map.put(gls, date, u)
+ end)
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}))
+ 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 ->
+ # %{date: date, gl: get_in(gls, [date, name])}
+ 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 = Nola.Account.get(user_id)
+
if account do
ds = Nola.Plugins.Alcoolog.data_state()
meta = Nola.Plugins.Alcoolog.get_user_meta(ds, account.id)
+
case Float.parse(value) do
{val, _} ->
new_meta = Map.put(meta, String.to_existing_atom(key), val)
Nola.Plugins.Alcoolog.put_user_meta(ds, account.id, new_meta)
+
_ ->
conn
|> put_status(:unprocessable_entity)
@@ -319,5 +436,4 @@ defmodule NolaWeb.AlcoologController do
|> text("not found")
end
end
-
end
diff --git a/lib/web/controllers/icecast_see_controller.ex b/lib/web/controllers/icecast_see_controller.ex
index 877ad4e..ca8fb2d 100644
--- a/lib/web/controllers/icecast_see_controller.ex
+++ b/lib/web/controllers/icecast_see_controller.ex
@@ -11,7 +11,7 @@ defmodule NolaWeb.IcecastSseController do
|> send_chunked(200)
|> subscribe
|> send_sse_message("ping", "ping")
- |> send_sse_message("icecast", Nola.IcecastAgent.get)
+ |> send_sse_message("icecast", Nola.IcecastAgent.get())
|> sse_loop
end
@@ -22,10 +22,11 @@ defmodule NolaWeb.IcecastSseController do
end
def sse_loop(conn) do
- {type, event} = receive do
- {:event, :ping} -> {"ping", "ping"}
- {:icecast, stats} -> {"icecast", stats}
- end
+ {type, event} =
+ receive do
+ {:event, :ping} -> {"ping", "ping"}
+ {:icecast, stats} -> {"icecast", stats}
+ end
conn
|> send_sse_message(type, event)
@@ -37,5 +38,4 @@ defmodule NolaWeb.IcecastSseController do
{:ok, conn} = chunk(conn, "event: #{type}\ndata: #{json}\n\n")
conn
end
-
end
diff --git a/lib/web/controllers/irc_auth_sse_controller.ex b/lib/web/controllers/irc_auth_sse_controller.ex
index 01c840b..f67a77f 100644
--- a/lib/web/controllers/irc_auth_sse_controller.ex
+++ b/lib/web/controllers/irc_auth_sse_controller.ex
@@ -6,12 +6,15 @@ defmodule NolaWeb.IrcAuthSseController do
@expire_delay :timer.minutes(3)
def sse(conn, params) do
- perks = if uri = Map.get(params, "redirect_to") do
- {:redirect, uri}
- else
- nil
- end
+ perks =
+ if uri = Map.get(params, "redirect_to") do
+ {:redirect, uri}
+ else
+ nil
+ end
+
token = String.downcase(EntropyString.random_string(65))
+
conn
|> assign(:token, token)
|> assign(:perks, perks)
@@ -31,25 +34,33 @@ defmodule NolaWeb.IrcAuthSseController do
end
def sse_loop(conn) do
- {type, event, exit} = receive do
- {:event, :ping} -> {"ping", "ping", false}
- {:event, :expire} -> {"expire", "expire", true}
- {:irc, :text, %{account: account, text: token} = m} ->
- if String.downcase(String.trim(token)) == conn.assigns.token do
- path = Nola.AuthToken.new_path(account.id, conn.assigns.perks)
- m.replyfun.("ok!")
- {"authenticated", path, true}
- else
+ {type, event, exit} =
+ receive do
+ {:event, :ping} ->
+ {"ping", "ping", false}
+
+ {:event, :expire} ->
+ {"expire", "expire", true}
+
+ {:irc, :text, %{account: account, text: token} = m} ->
+ if String.downcase(String.trim(token)) == conn.assigns.token do
+ path = Nola.AuthToken.new_path(account.id, conn.assigns.perks)
+ m.replyfun.("ok!")
+ {"authenticated", path, true}
+ else
+ {nil, nil, false}
+ end
+
+ _ ->
{nil, nil, false}
- end
- _ -> {nil, nil, false}
- end
+ end
- conn = if type do
- send_sse_message(conn, type, event)
- else
- conn
- end
+ conn =
+ if type do
+ send_sse_message(conn, type, event)
+ else
+ conn
+ end
if exit do
conn
@@ -62,5 +73,4 @@ defmodule NolaWeb.IrcAuthSseController do
{:ok, conn} = chunk(conn, "event: #{type}\ndata: #{data}\n\n")
conn
end
-
end
diff --git a/lib/web/controllers/irc_controller.ex b/lib/web/controllers/irc_controller.ex
index a78582e..9de807b 100644
--- a/lib/web/controllers/irc_controller.ex
+++ b/lib/web/controllers/irc_controller.ex
@@ -1,31 +1,46 @@
defmodule NolaWeb.IrcController do
use NolaWeb, :controller
- plug NolaWeb.ContextPlug
+ plug(NolaWeb.ContextPlug)
def index(conn, params) do
network = Map.get(params, "network")
channel = if c = Map.get(params, "chan"), do: NolaWeb.reformat_chan(c)
- commands = for mod <- Enum.uniq([Nola.Plugins.Account] ++ Nola.Plugins.enabled()) do
- if is_atom(mod) do
- identifier = Module.split(mod) |> List.last |> Macro.underscore
- if Kernel.function_exported?(mod, :irc_doc, 0), do: {identifier, mod.irc_doc()}
+
+ commands =
+ for mod <- Enum.uniq([Nola.Plugins.Account] ++ Nola.Plugins.enabled()) do
+ if is_atom(mod) do
+ identifier = Module.split(mod) |> List.last() |> Macro.underscore()
+ if Kernel.function_exported?(mod, :irc_doc, 0), do: {identifier, mod.irc_doc()}
+ end
end
- end
- |> Enum.filter(& &1)
- |> Enum.filter(fn({_, doc}) -> doc end)
- members = cond do
- network && channel -> Enum.map(Nola.UserTrack.channel(network, channel), fn(tuple) -> Nola.UserTrack.User.from_tuple(tuple) end)
- true ->
- Nola.Membership.of_account(conn.assigns.account)
- end
- render conn, "index.html", network: network, commands: commands, channel: channel, members: members
+ |> Enum.filter(& &1)
+ |> Enum.filter(fn {_, doc} -> doc end)
+
+ members =
+ cond do
+ network && channel ->
+ Enum.map(Nola.UserTrack.channel(network, channel), fn tuple ->
+ Nola.UserTrack.User.from_tuple(tuple)
+ end)
+
+ true ->
+ Nola.Membership.of_account(conn.assigns.account)
+ end
+
+ render(conn, "index.html",
+ network: network,
+ commands: commands,
+ channel: channel,
+ members: members
+ )
end
def txt(conn, %{"name" => name}) do
if String.contains?(name, ".txt") do
name = String.replace(name, ".txt", "")
data = data()
+
if Map.has_key?(data, name) do
lines = Enum.join(data[name], "\n")
text(conn, lines)
@@ -38,32 +53,54 @@ defmodule NolaWeb.IrcController do
do_txt(conn, name)
end
end
- def txt(conn, _), do: do_txt(conn, nil)
+ def txt(conn, _), do: do_txt(conn, nil)
defp do_txt(conn, nil) do
doc = Nola.Plugins.Txt.irc_doc()
data = data()
- main = Enum.filter(data, fn({trigger, _}) -> !String.contains?(trigger, ".") end) |> Enum.into(Map.new)
- system = Enum.filter(data, fn({trigger, _}) -> String.contains?(trigger, ".") end) |> Enum.into(Map.new)
- lines = Enum.reduce(main, 0, fn({_, lines}, acc) -> acc + Enum.count(lines) end)
+
+ main =
+ Enum.filter(data, fn {trigger, _} -> !String.contains?(trigger, ".") end)
+ |> Enum.into(Map.new())
+
+ system =
+ Enum.filter(data, fn {trigger, _} -> String.contains?(trigger, ".") end)
+ |> Enum.into(Map.new())
+
+ lines = Enum.reduce(main, 0, fn {_, lines}, acc -> acc + Enum.count(lines) end)
+
conn
|> assign(:title, "txt")
- |> render("txts.html", data: main, doc: doc, files: Enum.count(main), lines: lines, system: system)
+ |> render("txts.html",
+ data: main,
+ doc: doc,
+ files: Enum.count(main),
+ lines: lines,
+ system: system
+ )
end
defp do_txt(conn, txt) do
data = data()
- base_url = cond do
- conn.assigns[:chan] -> "/#{conn.assigns.network}/#{NolaWeb.format_chan(conn.assigns.chan)}"
- true -> "/-"
- end
+
+ base_url =
+ cond do
+ conn.assigns[:chan] ->
+ "/#{conn.assigns.network}/#{NolaWeb.format_chan(conn.assigns.chan)}"
+
+ true ->
+ "/-"
+ end
+
if lines = Map.get(data, txt) do
- lines = Enum.map(lines, fn(line) ->
- line
- |> String.split("\\\\")
- |> Enum.intersperse(Phoenix.HTML.Tag.tag(:br))
- end)
+ 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")
@@ -77,25 +114,28 @@ defmodule NolaWeb.IrcController do
defp data() do
dir = Application.get_env(:nola, :data_path) <> "/irc.txt/"
+
Path.wildcard(dir <> "/*.txt")
- |> Enum.reduce(%{}, fn(path, m) ->
+ |> Enum.reduce(%{}, fn path, m ->
path = String.split(path, "/")
file = List.last(path)
key = String.replace(file, ".txt", "")
- data = dir <> file
- |> File.read!
- |> String.split("\n")
- |> Enum.reject(fn(line) ->
- cond do
- line == "" -> true
- !line -> true
- true -> false
- end
- end)
+
+ data =
+ (dir <> file)
+ |> File.read!()
+ |> String.split("\n")
+ |> Enum.reject(fn line ->
+ cond do
+ line == "" -> true
+ !line -> true
+ true -> false
+ end
+ end)
+
Map.put(m, key, data)
end)
- |> Enum.sort
- |> Enum.into(Map.new)
+ |> Enum.sort()
+ |> Enum.into(Map.new())
end
-
end
diff --git a/lib/web/controllers/network_controller.ex b/lib/web/controllers/network_controller.ex
index 800294f..07cc291 100644
--- a/lib/web/controllers/network_controller.ex
+++ b/lib/web/controllers/network_controller.ex
@@ -1,11 +1,10 @@
defmodule NolaWeb.NetworkController do
use NolaWeb, :controller
- plug NolaWeb.ContextPlug
+ plug(NolaWeb.ContextPlug)
def index(conn, %{"network" => network}) do
conn
|> assign(:title, network)
|> render("index.html")
end
-
end
diff --git a/lib/web/controllers/open_id_controller.ex b/lib/web/controllers/open_id_controller.ex
index 24dc1a5..b8ea505 100644
--- a/lib/web/controllers/open_id_controller.ex
+++ b/lib/web/controllers/open_id_controller.ex
@@ -1,10 +1,15 @@
defmodule NolaWeb.OpenIdController do
use NolaWeb, :controller
- plug NolaWeb.ContextPlug, restrict: :public
+ plug(NolaWeb.ContextPlug, restrict: :public)
require Logger
def login(conn, _) do
- url = OAuth2.Client.authorize_url!(new_client(), scope: "openid", state: Base.url_encode64(:crypto.strong_rand_bytes(32), padding: false))
+ url =
+ OAuth2.Client.authorize_url!(new_client(),
+ scope: "openid",
+ state: Base.url_encode64(:crypto.strong_rand_bytes(32), padding: false)
+ )
+
redirect(conn, external: url)
end
@@ -14,19 +19,21 @@ defmodule NolaWeb.OpenIdController do
end
def callback(conn, %{"code" => code, "state" => state}) do
- with \
- client = %{token: %OAuth2.AccessToken{access_token: json}} = OAuth2.Client.get_token!(new_client(), state: state, code: code),
+ with client =
+ %{token: %OAuth2.AccessToken{access_token: json}} =
+ OAuth2.Client.get_token!(new_client(), state: state, code: code),
{:ok, %{"access_token" => token}} <- Jason.decode(json),
- client = %OAuth2.Client{client | token: %OAuth2.AccessToken{access_token: token}},
+ client = %OAuth2.Client{client | token: %OAuth2.AccessToken{access_token: token}},
{:ok, %OAuth2.Response{body: body}} <- OAuth2.Client.get(client, "/userinfo"),
- {:ok, %{"sub" => id, "preferred_username" => username}} <- Jason.decode(body)
- do
+ {:ok, %{"sub" => id, "preferred_username" => username}} <- Jason.decode(body) do
if account = conn.assigns.account do
- if !Nola.Account.get_meta(account, "identity-id") do # XXX: And oidc id not linked yet
- Nola.Account.put_meta(account, "identity-id", id)
- end
- Nola.Account.put_meta(account, "identity-username", username)
- conn
+ # XXX: And oidc id not linked yet
+ if !Nola.Account.get_meta(account, "identity-id") do
+ Nola.Account.put_meta(account, "identity-id", id)
+ end
+
+ Nola.Account.put_meta(account, "identity-username", username)
+ conn
else
conn
end
@@ -39,8 +46,9 @@ defmodule NolaWeb.OpenIdController do
{:error, %OAuth2.Response{status_code: 401}} ->
Logger.error("OpenID: Unauthorized token")
render(conn, "error.html", error: "The token is invalid.")
+
{:error, %OAuth2.Error{reason: reason}} ->
- Logger.error("Error: #{inspect reason}")
+ Logger.error("Error: #{inspect(reason)}")
render(conn, "error.html", error: reason)
end
end
@@ -51,7 +59,8 @@ defmodule NolaWeb.OpenIdController do
defp new_client() do
config = Application.get_env(:nola, :oidc)
- OAuth2.Client.new([
+
+ OAuth2.Client.new(
strategy: OAuth2.Strategy.AuthCode,
client_id: config[:client_id],
client_secret: config[:client_secret],
@@ -59,6 +68,6 @@ defmodule NolaWeb.OpenIdController do
authorize_url: config[:authorize_url],
token_url: config[:token_url],
redirect_uri: Routes.open_id_url(NolaWeb.Endpoint, :callback)
- ])
+ )
end
end
diff --git a/lib/web/controllers/page_controller.ex b/lib/web/controllers/page_controller.ex
index cb46b8f..4f3c44e 100644
--- a/lib/web/controllers/page_controller.ex
+++ b/lib/web/controllers/page_controller.ex
@@ -1,15 +1,14 @@
defmodule NolaWeb.PageController do
use NolaWeb, :controller
- plug NolaWeb.ContextPlug when action not in [:token]
- plug NolaWeb.ContextPlug, [restrict: :public] when action in [:token]
+ plug(NolaWeb.ContextPlug when action not in [:token])
+ plug(NolaWeb.ContextPlug, [restrict: :public] when action in [:token])
def token(conn, %{"token" => token}) do
- with \
- {:ok, account, perks} <- Nola.AuthToken.lookup(token)
- do
- IO.puts("Authenticated account #{inspect account}")
+ with {:ok, account, perks} <- Nola.AuthToken.lookup(token) do
+ IO.puts("Authenticated account #{inspect(account)}")
conn = put_session(conn, :account, account)
+
case perks do
nil -> redirect(conn, to: "/")
{:redirect, path} -> redirect(conn, to: path)
@@ -27,27 +26,33 @@ defmodule NolaWeb.PageController do
users = Nola.UserTrack.find_by_account(account)
metas = Nola.Account.get_all_meta(account)
predicates = Nola.Account.get_predicates(account)
+
conn
|> assign(:title, account.name)
- |> render("user.html", users: users, memberships: memberships, metas: metas, predicates: predicates)
+ |> render("user.html",
+ users: users,
+ memberships: memberships,
+ metas: metas,
+ predicates: predicates
+ )
end
def irc(conn, _) do
- bot_helps = for mod <- Nola.Irc.env(:handlers) do
- mod.irc_doc()
- end
- render conn, "irc.html", bot_helps: bot_helps
+ bot_helps =
+ for mod <- Nola.Irc.env(:handlers) do
+ mod.irc_doc()
+ end
+
+ render(conn, "irc.html", bot_helps: bot_helps)
end
def authenticate(conn, _) do
- with \
- {:account, account_id} when is_binary(account_id) <- {:account, get_session(conn, :account)},
- {:account, account} when not is_nil(account) <- {:account, Nola.Account.get(account_id)}
- do
+ with {:account, account_id} when is_binary(account_id) <-
+ {:account, get_session(conn, :account)},
+ {:account, account} when not is_nil(account) <- {:account, Nola.Account.get(account_id)} do
assign(conn, :account, account)
else
_ -> conn
end
end
-
end
diff --git a/lib/web/controllers/sms_controller.ex b/lib/web/controllers/sms_controller.ex
index 0fffa23..20a58bd 100644
--- a/lib/web/controllers/sms_controller.ex
+++ b/lib/web/controllers/sms_controller.ex
@@ -3,8 +3,7 @@ defmodule NolaWeb.SmsController do
require Logger
def ovh_callback(conn, %{"senderid" => from, "message" => message}) do
- spawn(fn() -> Nola.Plugins.Sms.incoming(from, String.trim(message)) end)
+ spawn(fn -> Nola.Plugins.Sms.incoming(from, String.trim(message)) end)
text(conn, "")
end
-
end
diff --git a/lib/web/controllers/untappd_controller.ex b/lib/web/controllers/untappd_controller.ex
index e2e1596..f47f526 100644
--- a/lib/web/controllers/untappd_controller.ex
+++ b/lib/web/controllers/untappd_controller.ex
@@ -2,11 +2,10 @@ defmodule NolaWeb.UntappdController do
use NolaWeb, :controller
def callback(conn, %{"code" => code}) do
- with \
- {:account, account_id} when is_binary(account_id) <- {:account, get_session(conn, :account)},
+ with {:account, account_id} when is_binary(account_id) <-
+ {:account, get_session(conn, :account)},
{:account, account} when not is_nil(account) <- {:account, Nola.Account.get(account_id)},
- {:ok, auth_token} <- Untappd.auth_callback(code)
- do
+ {:ok, auth_token} <- Untappd.auth_callback(code) do
Nola.Account.put_meta(account, "untappd-token", auth_token)
text(conn, "OK!")
else
@@ -14,5 +13,4 @@ defmodule NolaWeb.UntappdController do
:error -> text(conn, "Error: untappd authentication failed")
end
end
-
end
diff --git a/lib/web/live/chat_live.ex b/lib/web/live/chat_live.ex
index 6902250..0d45428 100644
--- a/lib/web/live/chat_live.ex
+++ b/lib/web/live/chat_live.ex
@@ -8,37 +8,46 @@ defmodule NolaWeb.ChatLive do
connection = Nola.Irc.Connection.get_network(network, chan)
account = Nola.Account.get(account_id)
membership = Nola.Membership.of_account(Nola.Account.get(account.id))
+
if account && connection && Enum.member?(membership, {connection.network, chan}) do
- {:ok, _} = Registry.register(Nola.PubSub, "#{connection.network}:events", plugin: __MODULE__)
+ {:ok, _} =
+ Registry.register(Nola.PubSub, "#{connection.network}:events", plugin: __MODULE__)
+
for t <- ["messages", "triggers", "outputs", "events"] do
- {:ok, _} = Registry.register(Nola.PubSub, "#{connection.network}/#{chan}:#{t}", plugin: __MODULE__)
+ {:ok, _} =
+ Registry.register(Nola.PubSub, "#{connection.network}/#{chan}:#{t}", plugin: __MODULE__)
end
Nola.Irc.PuppetConnection.start(account, connection)
- users = Nola.UserTrack.channel(connection.network, chan)
- |> Enum.map(fn(tuple) -> Nola.UserTrack.User.from_tuple(tuple) end)
- |> Enum.reduce(Map.new, fn(user = %{id: id}, acc) ->
- Map.put(acc, id, user)
- end)
-
- backlog = case Nola.Plugins.Buffer.select_buffer(connection.network, chan) do
- {backlog, _} ->
- {backlog, _} = Enum.reduce(backlog, {backlog, nil}, &reduce_contextual_event/2)
- Enum.reverse(backlog)
- _ -> []
- end
-
- socket = socket
- |> assign(:connection_id, connection.id)
- |> assign(:network, connection.network)
- |> assign(:chan, chan)
- |> assign(:title, "live")
- |> assign(:channel, chan)
- |> assign(:account_id, account.id)
- |> assign(:backlog, backlog)
- |> assign(:users, users)
- |> assign(:counter, 0)
+ users =
+ Nola.UserTrack.channel(connection.network, chan)
+ |> Enum.map(fn tuple -> Nola.UserTrack.User.from_tuple(tuple) end)
+ |> Enum.reduce(Map.new(), fn user = %{id: id}, acc ->
+ Map.put(acc, id, user)
+ end)
+
+ backlog =
+ case Nola.Plugins.Buffer.select_buffer(connection.network, chan) do
+ {backlog, _} ->
+ {backlog, _} = Enum.reduce(backlog, {backlog, nil}, &reduce_contextual_event/2)
+ Enum.reverse(backlog)
+
+ _ ->
+ []
+ end
+
+ socket =
+ socket
+ |> assign(:connection_id, connection.id)
+ |> assign(:network, connection.network)
+ |> assign(:chan, chan)
+ |> assign(:title, "live")
+ |> assign(:channel, chan)
+ |> assign(:account_id, account.id)
+ |> assign(:backlog, backlog)
+ |> assign(:users, users)
+ |> assign(:counter, 0)
{:ok, socket}
else
@@ -54,9 +63,11 @@ defmodule NolaWeb.ChatLive do
def handle_info({:irc, :event, event = %{type: :join, user_id: id}}, socket) do
if user = Nola.UserTrack.lookup(id) do
- socket = socket
- |> assign(:users, Map.put(socket.assigns.users, id, user))
- |> append_to_backlog(event)
+ socket =
+ socket
+ |> assign(:users, Map.put(socket.assigns.users, id, user))
+ |> append_to_backlog(event)
+
{:noreply, socket}
else
{:noreply, socket}
@@ -64,23 +75,29 @@ defmodule NolaWeb.ChatLive do
end
def handle_info({:irc, :event, event = %{type: :nick, user_id: id, nick: nick}}, socket) do
- socket = socket
- |> assign(:users, update_in(socket.assigns.users, [id, :nick], nick))
- |> append_to_backlog(event)
+ socket =
+ socket
+ |> assign(:users, update_in(socket.assigns.users, [id, :nick], nick))
+ |> append_to_backlog(event)
+
{:noreply, socket}
end
def handle_info({:irc, :event, event = %{type: :quit, user_id: id}}, socket) do
- socket = socket
- |> assign(:users, Map.delete(socket.assigns.users, id))
- |> append_to_backlog(event)
+ socket =
+ socket
+ |> assign(:users, Map.delete(socket.assigns.users, id))
+ |> append_to_backlog(event)
+
{:noreply, socket}
end
def handle_info({:irc, :event, event = %{type: :part, user_id: id}}, socket) do
- socket = socket
- |> assign(:users, Map.delete(socket.assigns.users, id))
- |> append_to_backlog(event)
+ socket =
+ socket
+ |> assign(:users, Map.delete(socket.assigns.users, id))
+ |> append_to_backlog(event)
+
{:noreply, socket}
end
@@ -91,13 +108,16 @@ defmodule NolaWeb.ChatLive do
# type is text, out, or nil if it's self?
def handle_info({:irc, type, message = %Nola.Message{}}, socket) do
IO.inspect({:live_message, type, message})
- socket = socket
- |> append_to_backlog(message)
+
+ socket =
+ socket
+ |> append_to_backlog(message)
+
{:noreply, socket}
end
def handle_info(info, socket) do
- Logger.debug("Unhandled info: #{inspect info}")
+ Logger.debug("Unhandled info: #{inspect(info)}")
{:noreply, socket}
end
@@ -109,13 +129,12 @@ defmodule NolaWeb.ChatLive do
defp reduce_contextual_event(line, {acc, nil}) do
{[line | acc], line}
end
+
defp reduce_contextual_event(line, {acc, last}) do
if NaiveDateTime.to_date(last.at) != NaiveDateTime.to_date(line.at) do
{[%{type: :day_changed, date: NaiveDateTime.to_date(line.at), at: nil}, line | acc], line}
- else
- {[line | acc], line}
- end
-
+ else
+ {[line | acc], line}
+ end
end
-
end
diff --git a/lib/web/router.ex b/lib/web/router.ex
index fb0df63..18509ac 100644
--- a/lib/web/router.ex
+++ b/lib/web/router.ex
@@ -2,88 +2,86 @@ defmodule NolaWeb.Router do
use NolaWeb, :router
pipeline :browser do
- plug :accepts, ["html", "txt"]
- plug :fetch_session
- plug :fetch_flash
- plug :fetch_live_flash
- plug :protect_from_forgery
- plug :put_secure_browser_headers
- plug :put_root_layout, {NolaWeb.LayoutView, :root}
+ plug(:accepts, ["html", "txt"])
+ plug(:fetch_session)
+ plug(:fetch_flash)
+ plug(:fetch_live_flash)
+ plug(:protect_from_forgery)
+ plug(:put_secure_browser_headers)
+ plug(:put_root_layout, {NolaWeb.LayoutView, :root})
end
pipeline :api do
- plug :accepts, ["json", "sse"]
+ plug(:accepts, ["json", "sse"])
end
pipeline :matrix_app_service do
- plug :accepts, ["json"]
- plug Nola.Matrix.Plug.Auth
- plug Nola.Matrix.Plug.SetConfig
+ plug(:accepts, ["json"])
+ plug(Nola.Matrix.Plug.Auth)
+ plug(Nola.Matrix.Plug.SetConfig)
end
scope "/api", NolaWeb do
- pipe_through :api
- get "/irc-auth.sse", IrcAuthSseController, :sse
- post "/sms/callback/Ovh", SmsController, :ovh_callback, as: :sms
+ pipe_through(:api)
+ get("/irc-auth.sse", IrcAuthSseController, :sse)
+ post("/sms/callback/Ovh", SmsController, :ovh_callback, as: :sms)
end
scope "/", NolaWeb do
- pipe_through :browser
- get "/", PageController, :index
+ pipe_through(:browser)
+ get("/", PageController, :index)
- get "/login/irc/:token", PageController, :token, as: :login
- get "/login/oidc", OpenIdController, :login
- get "/login/oidc/callback", OpenIdController, :callback
+ get("/login/irc/:token", PageController, :token, as: :login)
+ get("/login/oidc", OpenIdController, :login)
+ get("/login/oidc/callback", OpenIdController, :callback)
- get "/api/untappd/callback", UntappdController, :callback, as: :untappd_callback
+ get("/api/untappd/callback", UntappdController, :callback, as: :untappd_callback)
- get "/-", IrcController, :index
- get "/-/txt", IrcController, :txt
- get "/-/txt/:name", IrcController, :txt
+ get("/-", IrcController, :index)
+ get("/-/txt", IrcController, :txt)
+ get("/-/txt/:name", IrcController, :txt)
- get "/-/gpt", GptController, :index
- get "/-/gpt/p/:id", GptController, :task
- get "/-/gpt/r/:id", GptController, :result
+ get("/-/gpt", GptController, :index)
+ get("/-/gpt/p/:id", GptController, :task)
+ get("/-/gpt/r/:id", GptController, :result)
- get "/-/alcoolog", AlcoologController, :index
- get "/-/alcoolog/~/:account_name", AlcoologController, :index
+ get("/-/alcoolog", AlcoologController, :index)
+ get("/-/alcoolog/~/:account_name", AlcoologController, :index)
- get "/:network", NetworkController, :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/~: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
+ 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
+ put("/api/alcoolog/minisync/:user_id/meta/:key", AlcoologController, :minisync_put_meta)
- get "/:network/:chan", IrcController, :index
- live "/:network/:chan/live", ChatLive
- get "/:network/:chan/txt", IrcController, :txt
- get "/:network/:chan/txt/:name", IrcController, :txt
- get "/:network/:channel/preums", IrcController, :preums
- get "/:network/:chan/alcoolog/t/:token", AlcoologController, :token
+ get("/:network/:chan", IrcController, :index)
+ live("/:network/:chan/live", ChatLive)
+ get("/:network/:chan/txt", IrcController, :txt)
+ get("/:network/:chan/txt/:name", IrcController, :txt)
+ get("/:network/:channel/preums", IrcController, :preums)
+ get("/:network/:chan/alcoolog/t/:token", AlcoologController, :token)
end
scope "/_matrix/:appservice", MatrixAppServiceWeb.V1, as: :matrix do
- pipe_through :matrix_app_service
+ pipe_through(:matrix_app_service)
- put "/transactions/:txn_id", TransactionController, :push
+ put("/transactions/:txn_id", TransactionController, :push)
- get "/users/:user_id", UserController, :query
- get "/rooms/*room_alias", RoomController, :query
+ get("/users/:user_id", UserController, :query)
+ get("/rooms/*room_alias", RoomController, :query)
- get "/thirdparty/protocol/:protocol", ThirdPartyController, :query_protocol
- get "/thirdparty/user/:protocol", ThirdPartyController, :query_users
- get "/thirdparty/location/:protocol", ThirdPartyController, :query_locations
- get "/thirdparty/location", ThirdPartyController, :query_location_by_alias
- get "/thirdparty/user", ThirdPartyController, :query_user_by_id
+ get("/thirdparty/protocol/:protocol", ThirdPartyController, :query_protocol)
+ get("/thirdparty/user/:protocol", ThirdPartyController, :query_users)
+ get("/thirdparty/location/:protocol", ThirdPartyController, :query_locations)
+ get("/thirdparty/location", ThirdPartyController, :query_location_by_alias)
+ get("/thirdparty/user", ThirdPartyController, :query_user_by_id)
end
-
-
end
diff --git a/lib/web/views/alcoolog_view.ex b/lib/web/views/alcoolog_view.ex
index ad52472..3a86038 100644
--- a/lib/web/views/alcoolog_view.ex
+++ b/lib/web/views/alcoolog_view.ex
@@ -1,6 +1,4 @@
defmodule NolaWeb.AlcoologView do
use NolaWeb, :view
require Integer
-
end
-
diff --git a/lib/web/views/error_helpers.ex b/lib/web/views/error_helpers.ex
index 25214bd..156b801 100644
--- a/lib/web/views/error_helpers.ex
+++ b/lib/web/views/error_helpers.ex
@@ -9,8 +9,8 @@ defmodule NolaWeb.ErrorHelpers do
Generates tag for inlined form input errors.
"""
def error_tag(form, field) do
- Enum.map(Keyword.get_values(form.errors, field), fn (error) ->
- content_tag :span, translate_error(error), class: "help-block"
+ Enum.map(Keyword.get_values(form.errors, field), fn error ->
+ content_tag(:span, translate_error(error), class: "help-block")
end)
end
diff --git a/lib/web/views/error_view.ex b/lib/web/views/error_view.ex
index 5cad939..5acd333 100644
--- a/lib/web/views/error_view.ex
+++ b/lib/web/views/error_view.ex
@@ -12,6 +12,6 @@ defmodule NolaWeb.ErrorView do
# In case no render clause matches or no
# template is found, let's render it as 500
def template_not_found(_template, assigns) do
- render "500.html", assigns
+ render("500.html", assigns)
end
end
diff --git a/lib/web/views/layout_view.ex b/lib/web/views/layout_view.ex
index 663eccf..747740f 100644
--- a/lib/web/views/layout_view.ex
+++ b/lib/web/views/layout_view.ex
@@ -2,17 +2,27 @@ defmodule NolaWeb.LayoutView do
use NolaWeb, :view
def liquid_markdown(conn, text) do
- context_path = cond do
- conn.assigns[:chan] -> "/#{conn.assigns[:network]}/#{NolaWeb.format_chan(conn.assigns[:chan])}"
- conn.assigns[:network] -> "/#{conn.assigns[:network]}/-"
- true -> "/-"
- end
+ context_path =
+ cond do
+ conn.assigns[:chan] ->
+ "/#{conn.assigns[:network]}/#{NolaWeb.format_chan(conn.assigns[:chan])}"
+
+ conn.assigns[:network] ->
+ "/#{conn.assigns[:network]}/-"
+
+ true ->
+ "/-"
+ end
{:ok, ast} = Liquex.parse(text)
- context = Liquex.Context.new(%{
- "context_path" => context_path
- })
+
+ context =
+ Liquex.Context.new(%{
+ "context_path" => context_path
+ })
+
{content, _} = Liquex.render(ast, context)
+
content
|> to_string()
|> Earmark.as_html!()
@@ -20,21 +30,28 @@ defmodule NolaWeb.LayoutView do
end
def page_title(conn) do
- target = cond do
- conn.assigns[:chan] ->
- "#{conn.assigns.chan} @ #{conn.assigns.network}"
- conn.assigns[:network] -> conn.assigns.network
- true -> Nola.name()
- end
-
- breadcrumb_title = Enum.map(Map.get(conn.assigns, :breadcrumbs)||[], fn({title, _href}) -> title end)
-
- title = [conn.assigns[:title], breadcrumb_title, target]
- |> List.flatten()
- |> Enum.uniq()
- |> Enum.filter(fn(x) -> x end)
- |> Enum.intersperse(" / ")
- |> Enum.join()
+ target =
+ cond do
+ conn.assigns[:chan] ->
+ "#{conn.assigns.chan} @ #{conn.assigns.network}"
+
+ conn.assigns[:network] ->
+ conn.assigns.network
+
+ true ->
+ Nola.name()
+ end
+
+ breadcrumb_title =
+ Enum.map(Map.get(conn.assigns, :breadcrumbs) || [], fn {title, _href} -> title end)
+
+ title =
+ [conn.assigns[:title], breadcrumb_title, target]
+ |> List.flatten()
+ |> Enum.uniq()
+ |> Enum.filter(fn x -> x end)
+ |> Enum.intersperse(" / ")
+ |> Enum.join()
content_tag(:title, title)
end
@@ -42,14 +59,16 @@ defmodule NolaWeb.LayoutView do
def format_time(date, with_relative \\ true) do
alias Timex.Format.DateTime.Formatters
alias Timex.Timezone
- date = if is_integer(date) do
- date
- |> DateTime.from_unix!(:millisecond)
- |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
- else
- date
- |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
- end
+
+ date =
+ if is_integer(date) do
+ date
+ |> DateTime.from_unix!(:millisecond)
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ else
+ date
+ |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase)
+ end
now = DateTime.now!("Europe/Paris", Tzdata.TimeZoneDatabase)
@@ -57,25 +76,39 @@ defmodule NolaWeb.LayoutView do
date_week = Timex.iso_week(date)
{y, w} = now_week
- now_last_week = {y, w-1}
- now_last_roll = 7-Timex.days_to_beginning_of_week(now)
+ 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_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.year == date.year && now.month == date.month) -> "{WDfull} {D} {h24}:{m}"
- true -> "{WDfull} {D} {M} {h24}:{m}"
- end
+ format =
+ cond do
+ date.year != now.year ->
+ "{D}/{M}/{YYYY} {h24}:{m}"
- {:ok, relative} = Formatters.Relative.relative_to(date, Timex.now("Europe/Paris"), "{relative}", "fr")
- {:ok, full} = Formatters.Default.lformat(date, "{WDfull} {D} {YYYY} {h24}:{m}", "fr") #"{h24}:{m} {WDfull} {D}", "fr")
- {:ok, detail} = Formatters.Default.lformat(date, format, "fr") #"{h24}:{m} {WDfull} {D}", "fr")
+ date_date == now_date ->
+ "{h24}:{m}"
- content_tag(:time, if(with_relative, do: relative, else: detail), [title: full])
- end
+ now_week == date_week ||
+ (date_week == now_last_week && Date.day_of_week(date) >= now_last_roll) ->
+ "{WDfull} {h24}:{m}"
+
+ now.year == date.year && now.month == date.month ->
+ "{WDfull} {D} {h24}:{m}"
+ true ->
+ "{WDfull} {D} {M} {h24}:{m}"
+ end
+
+ {:ok, relative} =
+ Formatters.Relative.relative_to(date, Timex.now("Europe/Paris"), "{relative}", "fr")
+
+ # "{h24}:{m} {WDfull} {D}", "fr")
+ {:ok, full} = Formatters.Default.lformat(date, "{WDfull} {D} {YYYY} {h24}:{m}", "fr")
+ # "{h24}:{m} {WDfull} {D}", "fr")
+ {:ok, detail} = Formatters.Default.lformat(date, format, "fr")
+
+ content_tag(:time, if(with_relative, do: relative, else: detail), title: full)
+ end
end
diff --git a/lib/web/views/network_view.ex b/lib/web/views/network_view.ex
index 7a24db1..58b0e55 100644
--- a/lib/web/views/network_view.ex
+++ b/lib/web/views/network_view.ex
@@ -1,4 +1,3 @@
defmodule NolaWeb.NetworkView do
use NolaWeb, :view
-
end
diff --git a/lib/web/views/open_id_view.ex b/lib/web/views/open_id_view.ex
index bd8089b..b847202 100644
--- a/lib/web/views/open_id_view.ex
+++ b/lib/web/views/open_id_view.ex
@@ -1,4 +1,3 @@
defmodule NolaWeb.OpenIdView do
use NolaWeb, :view
-
end