summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/irc.ex34
-rw-r--r--lib/irc/connection_handler.ex (renamed from lib/lsg_irc/connection_handler.ex)3
-rw-r--r--lib/irc/login_handler.ex (renamed from lib/lsg_irc/login_handler.ex)2
-rw-r--r--lib/irc/pubsub_handler.ex123
-rw-r--r--lib/irc/user_track.ex (renamed from lib/lsg_irc/user_track.ex)12
-rw-r--r--lib/irc/user_track_handler.ex (renamed from lib/lsg_irc/user_track_handler.ex)16
-rw-r--r--lib/lsg/application.ex2
-rw-r--r--lib/lsg_irc.ex24
-rw-r--r--lib/lsg_irc/admin_handler.ex15
-rw-r--r--lib/lsg_irc/base_handler.ex2
-rw-r--r--lib/lsg_irc/calc_handler.ex38
-rw-r--r--lib/lsg_irc/calc_plugin.ex38
-rw-r--r--lib/lsg_irc/dice_handler.ex1
-rw-r--r--lib/lsg_irc/handler.ex24
-rw-r--r--lib/lsg_irc/kick_roulette_handler.ex2
-rw-r--r--lib/lsg_irc/last_fm_handler.ex8
-rw-r--r--lib/lsg_irc/quatre_cent_vingt_plugin.ex105
-rw-r--r--lib/lsg_irc/txt_handler.ex14
-rw-r--r--lib/lsg_irc/wikipedia_plugin.ex90
-rw-r--r--lib/lsg_irc/youtube_handler.ex2
-rw-r--r--lib/lsg_web/controllers/irc_controller.ex2
21 files changed, 454 insertions, 103 deletions
diff --git a/lib/irc.ex b/lib/irc.ex
new file mode 100644
index 0000000..6ba819c
--- /dev/null
+++ b/lib/irc.ex
@@ -0,0 +1,34 @@
+defmodule IRC do
+
+ defmodule Message do
+ defstruct [:text,
+ :sender,
+ :channel,
+ :trigger,
+ :replyfun]
+ end
+ defmodule Trigger do
+ defstruct [:type, :trigger, :args]
+ end
+
+ def register(key) do
+ case Registry.register(IRC.PubSub, key, []) do
+ {:ok, _} -> :ok
+ error -> error
+ end
+ end
+
+ def admin?(%Message{sender: sender}), do: admin?(sender)
+
+ def admin?(%{nick: nick, user: user, host: host}) do
+ for {n, u, h} <- Application.get_env(:lsg, :irc, [])[:admins]||[] do
+ admin_part_match?(n, nick) && admin_part_match?(u, user) && admin_part_match?(h, host)
+ end
+ |> Enum.any?
+ end
+
+ defp admin_part_match?(:_, _), do: true
+ defp admin_part_match?(a, a), do: true
+ defp admin_part_match?(_, _), do: false
+
+end
diff --git a/lib/lsg_irc/connection_handler.ex b/lib/irc/connection_handler.ex
index 8d07e58..1c335f2 100644
--- a/lib/lsg_irc/connection_handler.ex
+++ b/lib/irc/connection_handler.ex
@@ -1,4 +1,4 @@
-defmodule LSG.IRC.ConnectionHandler do
+defmodule IRC.ConnectionHandler do
defmodule State do
defstruct [:host, :port, :pass, :nick, :name, :user, :client]
end
@@ -14,7 +14,6 @@ defmodule LSG.IRC.ConnectionHandler do
end
def init([state]) do
- IO.puts inspect(state)
ExIRC.Client.add_handler state.client, self
ExIRC.Client.connect! state.client, state.host, state.port
{:ok, state}
diff --git a/lib/lsg_irc/login_handler.ex b/lib/irc/login_handler.ex
index f989b40..fdec852 100644
--- a/lib/lsg_irc/login_handler.ex
+++ b/lib/irc/login_handler.ex
@@ -1,4 +1,4 @@
-defmodule LSG.IRC.LoginHandler do
+defmodule IRC.LoginHandler do
def start_link(client) do
GenServer.start_link(__MODULE__, [client])
end
diff --git a/lib/irc/pubsub_handler.ex b/lib/irc/pubsub_handler.ex
new file mode 100644
index 0000000..3e78d7b
--- /dev/null
+++ b/lib/irc/pubsub_handler.ex
@@ -0,0 +1,123 @@
+defmodule IRC.PubSubHandler do
+ @moduledoc """
+ # IRC PubSub
+
+ Provides a nicer abstraction over ExIRC's handlers.
+
+ ## PubSub topics
+
+ * `message` -- all messages (including triggers)
+ * `message:private` -- all messages without a channel
+ * `message:#CHANNEL` -- all messages within `#CHANNEL`
+ * `triggers` -- all triggers
+ * `trigger:TRIGGER` -- any message with a trigger `TRIGGER`
+
+ ## Replying to %IRC.Message{}
+
+ Each `IRC.Message` comes with a dedicated `replyfun`, to which you only have to pass either:
+
+ """
+ def irc_doc, do: nil
+
+ def start_link(client) do
+ GenServer.start_link(__MODULE__, [client], [name: __MODULE__])
+ end
+
+ def init([client]) do
+ ExIRC.Client.add_handler(client, self())
+ {:ok, client}
+ end
+
+ @triggers %{
+ "!" => :bang,
+ "+" => :plus,
+ "-" => :minus,
+ "?" => :query,
+ "." => :dot,
+ }
+
+ def handle_info({:received, text, sender, chan}, client) do
+ reply_fun = fn(text) -> irc_reply(client, {chan, sender}, text) end
+ message = %IRC.Message{text: text, sender: sender, channel: chan, replyfun: reply_fun, trigger: extract_trigger(text)}
+ publish(message, ["message:#{chan}"])
+ {:noreply, client}
+ end
+
+ def handle_info({:received, text, sender}, client) do
+ reply_fun = fn(text) -> irc_reply(client, {sender.nick, sender}, text) end
+ message = %IRC.Message{text: text, sender: sender, replyfun: reply_fun, trigger: extract_trigger(text)}
+ publish(message, ["message:private"])
+ {:noreply, client}
+ end
+
+ def handle_info(unhandled, client) do
+ IO.puts inspect(unhandled)
+ {:noreply, client}
+ end
+
+ defp publish(pub), do: publish(pub, [])
+
+ defp publish(m = %IRC.Message{trigger: nil}, keys) do
+ dispatch(["message"] ++ keys, {:irc, :text, m})
+ end
+
+ defp publish(m = %IRC.Message{trigger: t = %IRC.Trigger{trigger: trigger}}, keys) do
+ dispatch(["message", "triggers", "trigger:"<>trigger]++keys, {:irc, :trigger, trigger, m})
+ end
+
+ defp dispatch(key, content) when is_binary(key), do: dispatch([key], content)
+ defp dispatch(keys, content) when is_list(keys) do
+ IO.puts "dispatching to #{inspect(keys)} --> #{inspect content}"
+ for key <- keys do
+ spawn(fn() -> Registry.dispatch(IRC.PubSub, key, fn h ->
+ for {pid, _} <- h, do: send(pid, content)
+ end) end)
+ end
+ end
+
+ #
+ # Triggers
+ #
+
+
+ for {trigger, name} <- @triggers do
+ defp extract_trigger(unquote(trigger)<>text) do
+ text = String.strip(text)
+ [trigger | args] = String.split(text, " ")
+ %IRC.Trigger{type: unquote(name), trigger: trigger, args: args}
+ end
+ end
+
+ defp extract_trigger(_), do: nil
+
+ #
+ # IRC Replies
+ #
+
+ # irc_reply(ExIRC.Client pid, {channel or nick, ExIRC.Sender}, binary | replies
+ # replies :: {:kick, reason} | {:kick, nick, reason} | {:mode, mode, nick}
+ defp irc_reply(client, {target, _}, text) when is_binary(text) do
+ ExIRC.Client.msg(client, :privmsg, target, text)
+ end
+
+ defp irc_reply(client, {target, %{nick: nick}}, {:kick, reason}) do
+ ExIRC.Client.kick(client, target, nick, reason)
+ end
+
+ defp irc_reply(client, {target, _}, {:kick, nick, reason}) do
+ ExIRC.Client.kick(client, target, nick, reason)
+ end
+
+ defp irc_reply(client, {target, %{nick: nick}}, {:mode, mode}) do
+ ExIRC.Client.mode(client, target, mode, nick)
+ end
+
+ defp irc_reply(client, target, {:mode, mode, nick}) do
+ ExIRC.Client.mode(client, target, mode, nick)
+ end
+
+ defp irc_reply(client, target, {:channel_mode, mode}) do
+ ExIRC.Client.mode(client, target, mode)
+ end
+
+end
diff --git a/lib/lsg_irc/user_track.ex b/lib/irc/user_track.ex
index b67b9f6..2614d98 100644
--- a/lib/lsg_irc/user_track.ex
+++ b/lib/irc/user_track.ex
@@ -1,9 +1,9 @@
-defmodule LSG.IRC.UserTrack do
+defmodule IRC.UserTrack do
@moduledoc """
User Track DB & Utilities
"""
- @ets LSG.IRC.UserTrack.Storage
+ @ets IRC.UserTrack.Storage
# {uuid, nick, nicks, privilege_map}
# Privilege map:
# %{"#channel" => [:operator, :voice]
@@ -40,6 +40,10 @@ defmodule LSG.IRC.UserTrack do
end
{:reply, returned, ets}
end
+
+ def terminate(_reason, ets) do
+ :ok
+ end
end
defmodule Id, do: use EntropyString
@@ -48,7 +52,7 @@ defmodule LSG.IRC.UserTrack do
defstruct [:id, :nick, :nicks, :username, :host, :realname, :privileges]
def to_tuple(u = %__MODULE__{}) do
- {u.id || LSG.IRC.UserTrack.Id.large_id, u.nick, u.nicks || [], u.username, u.host, u.realname, u.privileges}
+ {u.id || IRC.UserTrack.Id.large_id, u.nick, u.nicks || [], u.username, u.host, u.realname, u.privileges}
end
def from_tuple({id, nick, nicks, username, host, realname, privs}) do
@@ -84,7 +88,7 @@ defmodule LSG.IRC.UserTrack do
def joined(c, s), do: joined(c,s,[])
def joined(channel, sender=%{nick: nick, user: uname, host: host}, privileges) do
- privileges = if LSG.IRC.admin?(sender) do
+ privileges = if IRC.admin?(sender) do
privileges ++ [:admin]
else privileges end
user = if user = find_by_nick(nick) do
diff --git a/lib/lsg_irc/user_track_handler.ex b/lib/irc/user_track_handler.ex
index d167af5..0ae802a 100644
--- a/lib/lsg_irc/user_track_handler.ex
+++ b/lib/irc/user_track_handler.ex
@@ -1,4 +1,4 @@
-defmodule LSG.IRC.UserTrackHandler do
+defmodule IRC.UserTrackHandler do
@moduledoc """
# User Track Handler
@@ -31,18 +31,18 @@ defmodule LSG.IRC.UserTrackHandler do
def handle_info({:who, channel, whos}, state) do
Enum.map(whos, fn(who = %ExIRC.Who{nick: nick, operator?: operator}) ->
priv = if operator, do: [:operator], else: []
- LSG.IRC.UserTrack.joined(channel, who, priv)
+ IRC.UserTrack.joined(channel, who, priv)
end)
{:noreply, state}
end
def handle_info({:quit, _reason, sender}, state) do
- LSG.IRC.UserTrack.quitted(sender)
+ IRC.UserTrack.quitted(sender)
{:noreply, state}
end
def handle_info({:joined, channel, sender}, state) do
- LSG.IRC.UserTrack.joined(channel, sender, [])
+ IRC.UserTrack.joined(channel, sender, [])
{:noreply, state}
end
@@ -71,22 +71,22 @@ defmodule LSG.IRC.UserTrackHandler do
end
defp parted(channel, nick) do
- LSG.IRC.UserTrack.parted(channel, nick)
+ IRC.UserTrack.parted(channel, nick)
:ok
end
defp mode(channel, nick, "+o") do
- LSG.IRC.UserTrack.change_privileges(channel, nick, {[:operator], []})
+ IRC.UserTrack.change_privileges(channel, nick, {[:operator], []})
:ok
end
defp mode(channel, nick, "-o") do
- LSG.IRC.UserTrack.change_privileges(channel, nick, {[], [:operator]})
+ IRC.UserTrack.change_privileges(channel, nick, {[], [:operator]})
:ok
end
defp rename(old, new) do
- LSG.IRC.UserTrack.renamed(old, new)
+ IRC.UserTrack.renamed(old, new)
:ok
end
diff --git a/lib/lsg/application.ex b/lib/lsg/application.ex
index 23db221..cc4c120 100644
--- a/lib/lsg/application.ex
+++ b/lib/lsg/application.ex
@@ -12,7 +12,7 @@ defmodule LSG.Application do
supervisor(LSGWeb.Endpoint, []),
# Start your own worker by calling: LSG.Worker.start_link(arg1, arg2, arg3)
# worker(LSG.Worker, [arg1, arg2, arg3]),
- worker(Registry, [[keys: :duplicate, name: LSG.BroadcastRegistry]]),
+ worker(Registry, [[keys: :duplicate, name: LSG.BroadcastRegistry]], id: :registry_broadcast),
worker(LSG.IcecastAgent, []),
worker(LSG.Icecast, []),
] ++ LSG.IRC.application_childs
diff --git a/lib/lsg_irc.ex b/lib/lsg_irc.ex
index b988e04..482cb5d 100644
--- a/lib/lsg_irc.ex
+++ b/lib/lsg_irc.ex
@@ -4,26 +4,22 @@ defmodule LSG.IRC do
{:ok, irc_client} = ExIRC.start_link!
import Supervisor.Spec
[
- worker(LSG.IRC.UserTrack.Storage, []),
- worker(LSG.IRC.ConnectionHandler, [irc_client]),
- worker(LSG.IRC.LoginHandler, [irc_client]),
- worker(LSG.IRC.UserTrackHandler, [irc_client]),
+ worker(Registry, [[keys: :duplicate, name: IRC.PubSub]], id: :registry_irc),
+ worker(IRC.UserTrack.Storage, []),
+ worker(IRC.ConnectionHandler, [irc_client]),
+ worker(IRC.LoginHandler, [irc_client]),
+ worker(IRC.UserTrackHandler, [irc_client]),
+ worker(IRC.PubSubHandler, [irc_client], [name: :irc_pub_sub]),
]
++
for handler <- Application.get_env(:lsg, :irc)[:handlers] do
- worker(handler, [irc_client])
+ worker(handler, [irc_client], [name: handler])
end
- end
-
- def admin?(%{nick: nick, user: user, host: host}) do
- for {n, u, h} <- Application.get_env(:lsg, :irc, [])[:admins]||[] do
- admin_part_match?(n, nick) && admin_part_match?(u, user) && admin_part_match?(h, host)
+ ++
+ for plugin <- Application.get_env(:lsg, :irc)[:plugins] do
+ worker(plugin, [], [name: plugin])
end
- |> Enum.any?
end
- defp admin_part_match?(:_, _), do: true
- defp admin_part_match?(a, a), do: true
- defp admin_part_match?(_, _), do: false
end
diff --git a/lib/lsg_irc/admin_handler.ex b/lib/lsg_irc/admin_handler.ex
index 27d35c3..fab4dbc 100644
--- a/lib/lsg_irc/admin_handler.ex
+++ b/lib/lsg_irc/admin_handler.ex
@@ -14,18 +14,27 @@ defmodule LSG.IRC.AdminHandler do
def init([client]) do
ExIRC.Client.add_handler client, self
+ :ok = IRC.register("op")
{:ok, client}
end
- def handle_info({:received, "!op", sender, chan}, client) do
- if LSG.IRC.admin?(sender) do
+ def handle_info({:irc, :trigger, "op", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang}, sender: sender}}, client) do
+ if IRC.admin?(sender) do
+ m.replyfun.({:mode, "+o"})
+ else
+ m.replyfun.({:kick, "non"})
+ end
+ {:noreply, client}
+ end
+
+ def handle_info({:joined, chan, sender}, client) do
+ if IRC.admin?(sender) do
ExIRC.Client.mode(client, chan, "+o", sender.nick)
end
{:noreply, client}
end
def handle_info(msg, client) do
- IO.inspect(msg)
{:noreply, client}
end
diff --git a/lib/lsg_irc/base_handler.ex b/lib/lsg_irc/base_handler.ex
index 9145936..35b2ade 100644
--- a/lib/lsg_irc/base_handler.ex
+++ b/lib/lsg_irc/base_handler.ex
@@ -1,7 +1,5 @@
defmodule LSG.IRC.BaseHandler do
- use LSG.IRC.Handler
-
def irc_doc, do: nil
def start_link(client) do
diff --git a/lib/lsg_irc/calc_handler.ex b/lib/lsg_irc/calc_handler.ex
deleted file mode 100644
index 0f74ac9..0000000
--- a/lib/lsg_irc/calc_handler.ex
+++ /dev/null
@@ -1,38 +0,0 @@
-defmodule LSG.IRC.CalcHandler do
- @moduledoc """
- # calc
-
- * **!calc `<expression>`**: évalue l'expression mathématique `<expression>`.
- """
-
- def irc_doc, do: @moduledoc
-
- def start_link(client) do
- GenServer.start_link(__MODULE__, [client])
- end
-
- def init([client]) do
- ExIRC.Client.add_handler client, self
- {:ok, client}
- end
-
- def handle_info({:received, "!calc "<>expr, %ExIRC.SenderInfo{nick: nick}, chan}, client) do
- IO.inspect "HAZ CALC " <>inspect(expr)
- result = try do
- case Abacus.eval(expr) do
- {:ok, result} -> result
- error -> inspect(error)
- end
- rescue
- error -> "#{error.message}"
- end
- ExIRC.Client.msg(client, :privmsg, chan, "#{nick}: #{expr} = #{result}")
- {:noreply, client}
- end
-
- def handle_info(msg, client) do
- {:noreply, client}
- end
-
-end
-
diff --git a/lib/lsg_irc/calc_plugin.ex b/lib/lsg_irc/calc_plugin.ex
new file mode 100644
index 0000000..6e4e30c
--- /dev/null
+++ b/lib/lsg_irc/calc_plugin.ex
@@ -0,0 +1,38 @@
+defmodule LSG.IRC.CalcPlugin do
+ @moduledoc """
+ # calc
+
+ * **!calc `<expression>`**: évalue l'expression mathématique `<expression>`.
+ """
+
+ def irc_doc, do: @moduledoc
+
+ def start_link() do
+ GenServer.start_link(__MODULE__, [])
+ end
+
+ def init(_) do
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:calc", [])
+ {:ok, nil}
+ end
+
+ def handle_info({:irc, :trigger, "calc", message = %IRC.Message{trigger: %IRC.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)
+ end
+ rescue
+ error -> "#{error.message}"
+ end
+ message.replyfun.("#{message.sender.nick}: #{expr} = #{result}")
+ {:noreply, state}
+ end
+
+ def handle_info(msg, state) do
+ {:noreply, state}
+ end
+
+end
+
diff --git a/lib/lsg_irc/dice_handler.ex b/lib/lsg_irc/dice_handler.ex
index b07b59b..7ff7b4d 100644
--- a/lib/lsg_irc/dice_handler.ex
+++ b/lib/lsg_irc/dice_handler.ex
@@ -22,6 +22,7 @@ defmodule LSG.IRC.DiceHandler do
def init([client]) do
ExIRC.Client.add_handler(client, self())
+ {:ok, _} = Registry.register(IRC.PubSub, "dice", [])
{:ok, %__MODULE__{client: client}}
end
diff --git a/lib/lsg_irc/handler.ex b/lib/lsg_irc/handler.ex
deleted file mode 100644
index 19c0945..0000000
--- a/lib/lsg_irc/handler.ex
+++ /dev/null
@@ -1,24 +0,0 @@
-defmodule LSG.IRC.Handler do
- defmacro __using__(_) do
- quote do
- alias LSG.IRC
- alias LSG.IRC.UserTrack
- alias ExIRC.Client
- require LSG.IRC.Handler
- import LSG.IRC.Handler
- end
- end
-
- def privmsg(client, {nil, %ExIRC.SenderInfo{nick: nick}}, message) do
- privmsg(client, nick, message)
- end
-
- def privmsg(client, {channel, _}, message) do
- privmsg(client, channel, message)
- end
-
- def privmsg(client, target, message) when is_binary(target) do
- ExIRC.Client.msg(client, :privmsg, target, message)
- end
-
-end
diff --git a/lib/lsg_irc/kick_roulette_handler.ex b/lib/lsg_irc/kick_roulette_handler.ex
index 3591b5e..ece1b95 100644
--- a/lib/lsg_irc/kick_roulette_handler.ex
+++ b/lib/lsg_irc/kick_roulette_handler.ex
@@ -18,7 +18,7 @@ defmodule LSG.IRC.KickRouletteHandler do
def handle_info({:received, "!kick", sender, chan}, client) do
if 5 == :crypto.rand_uniform(1, 6) do
spawn(fn() ->
- :timer.sleep(:crypto.rand_uniform(500, 15_000))
+ :timer.sleep(:crypto.rand_uniform(200, 10_000))
ExIRC.Client.kick(client, chan, sender.nick, "perdu")
end)
end
diff --git a/lib/lsg_irc/last_fm_handler.ex b/lib/lsg_irc/last_fm_handler.ex
index fe767b1..4eef24e 100644
--- a/lib/lsg_irc/last_fm_handler.ex
+++ b/lib/lsg_irc/last_fm_handler.ex
@@ -64,6 +64,14 @@ defmodule LSG.IRC.LastFmHandler do
{:noreply, state}
end
+ def terminate(_reason, state) do
+ if state.dets do
+ :dets.sync(state.dets)
+ :dets.close(state.dets)
+ end
+ :ok
+ end
+
defp irc_now_playing(nick_or_user, chan, state) do
nick_or_user = String.strip(nick_or_user)
username = case :dets.lookup(state.dets, String.downcase(nick_or_user)) do
diff --git a/lib/lsg_irc/quatre_cent_vingt_plugin.ex b/lib/lsg_irc/quatre_cent_vingt_plugin.ex
new file mode 100644
index 0000000..579858e
--- /dev/null
+++ b/lib/lsg_irc/quatre_cent_vingt_plugin.ex
@@ -0,0 +1,105 @@
+defmodule LSG.IRC.QuatreCentVingtPlugin do
+ require Logger
+
+ @moduledoc """
+ # 420
+
+ * **!420**: recorde un nouveau 420.
+ * **!420 pseudo**: stats du pseudo.
+ """
+
+ @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!!"]
+ }
+
+ @emojis [
+ "\\o/",
+ "~o~",
+ "~~o∞~~",
+ "*\\o/*",
+ "**\\o/**",
+ "*ô*",
+ ]
+
+ @coeffs Range.new(1, 10)
+
+ def irc_doc, do: @moduledoc
+
+ def start_link, do: GenServer.start_link(__MODULE__, [])
+
+ def init(_) do
+ for coeff <- @coeffs do
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:#{420*coeff}", [])
+ end
+ dets_filename = (LSG.data_path() <> "/" <> "420.dets") |> String.to_charlist
+ {:ok, dets} = :dets.open_file(dets_filename, [{:type,:bag}])
+ {:ok, dets}
+ end
+
+ for coeff <- @coeffs do
+ qvc = to_string(420 * coeff)
+ def handle_info({:irc, :trigger, unquote(qvc), m = %IRC.Message{trigger: %IRC.Trigger{args: [], type: :bang}}}, dets) do
+ {count, last} = get_statistics_for_nick(dets, m.sender.nick)
+ count = count + unquote(coeff)
+ text = achievement_text(count)
+ now = DateTime.to_unix(DateTime.utc_now())-1 # this is ugly
+ for i <- Range.new(1, unquote(coeff)) do
+ :ok = :dets.insert(dets, {String.downcase(m.sender.nick), 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 = %IRC.Message{trigger: %IRC.Trigger{args: [nick], type: :bang}}}, dets) do
+ text = case get_statistics_for_nick(dets, nick) 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)
+ {:noreply, dets}
+ 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")
+ {:ok, detail} = Formatters.Default.lformat(date, " ({h24}:{m})", "fr")
+
+ relative <> detail
+ end
+
+ defp get_statistics_for_nick(dets, nick) do
+ qvc = :dets.lookup(dets, String.downcase(nick)) |> Enum.sort
+ count = Enum.reduce(qvc, 0, fn(_, acc) -> acc + 1 end)
+ {_, last} = List.last(qvc) || {nil, nil}
+ {count, last}
+ end
+
+ @achievements_keys Map.keys(@achievements)
+ defp achievement_text(count) when count in @achievements_keys do
+ Enum.random(Map.get(@achievements, count))
+ end
+
+ defp achievement_text(count) do
+ emoji = Enum.random(@emojis)
+ "#{emoji} [#{count}]"
+ end
+
+end
diff --git a/lib/lsg_irc/txt_handler.ex b/lib/lsg_irc/txt_handler.ex
index efe2b68..032c11c 100644
--- a/lib/lsg_irc/txt_handler.ex
+++ b/lib/lsg_irc/txt_handler.ex
@@ -1,5 +1,5 @@
defmodule LSG.IRC.TxtHandler do
- alias LSG.IRC.UserTrack
+ alias IRC.UserTrack
require Logger
@moduledoc """
@@ -213,6 +213,14 @@ defmodule LSG.IRC.TxtHandler do
{:noreply, state}
end
+ def terminate(_reason, state) do
+ if state.locks 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")
@@ -331,8 +339,8 @@ defmodule LSG.IRC.TxtHandler do
end
defp can_write?(state = %__MODULE__{rw: rw?, locks: locks}, channel, sender, trigger) do
- admin? = LSG.IRC.admin?(sender)
- operator? = LSG.IRC.UserTrack.operator?(channel, sender.nick)
+ admin? = IRC.admin?(sender)
+ operator? = IRC.UserTrack.operator?(channel, sender.nick)
locked? = case :dets.lookup(locks, trigger) do
[{trigger}] -> true
_ -> false
diff --git a/lib/lsg_irc/wikipedia_plugin.ex b/lib/lsg_irc/wikipedia_plugin.ex
new file mode 100644
index 0000000..4ee95b1
--- /dev/null
+++ b/lib/lsg_irc/wikipedia_plugin.ex
@@ -0,0 +1,90 @@
+defmodule LSG.IRC.WikipediaPlugin do
+ require Logger
+
+ @moduledoc """
+ # wikipédia
+
+ * **!wp `<recherche>`**: retourne le premier résultat de la `<recherche>` Wikipedia
+ * **!wp**: un article Wikipédia au hasard
+ """
+
+ def irc_doc, do: @moduledoc
+
+ def start_link() do
+ GenServer.start_link(__MODULE__, [])
+ end
+
+ def init(_) do
+ {:ok, _} = Registry.register(IRC.PubSub, "trigger:wp", [])
+ {:ok, nil}
+ end
+
+ def handle_info({:irc, :trigger, "wp", message = %IRC.Message{trigger: %IRC.Trigger{args: []}}}, state) do
+ irc_random(message)
+ {:noreply, state}
+ end
+ def handle_info({:irc, :trigger, "wp", message = %IRC.Message{trigger: %IRC.Trigger{args: args}}}, state) do
+ irc_search(Enum.join(args, " "), message)
+ {:noreply, state}
+ end
+
+ def handle_info(info, state) do
+ {:noreply, state}
+ 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,
+ }
+ 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
+ end
+
+ defp irc_random(message) do
+ params = %{
+ "action" => "query",
+ "generator" => "random",
+ "grnnamespace" => 0,
+ "prop" => "info"
+ }
+ case query_wikipedia(params) do
+ {:ok, %{"query" => %{"pages" => map = %{}}}} ->
+ [{_, item}] = Map.to_list(map)
+ title = item["title"]
+ url = "https://fr.wikipedia.org/wiki/" <> String.replace(title, " ", "_")
+ msg = "Wikipédia: #{title} — #{url}"
+ message.replyfun.(msg)
+ _ ->
+ nil
+ end
+ end
+
+ defp query_wikipedia(params) do
+ url = "https://fr.wikipedia.org/w/api.php"
+ 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: 400, body: body}} ->
+ Logger.error "Wikipedia HTTP 400: #{inspect body}"
+ {:error, "http 400"}
+ error ->
+ Logger.error "Wikipedia http error: #{inspect error}"
+ {:error, "http client error"}
+ end
+ end
+
+end
diff --git a/lib/lsg_irc/youtube_handler.ex b/lib/lsg_irc/youtube_handler.ex
index e7eadfc..51584a2 100644
--- a/lib/lsg_irc/youtube_handler.ex
+++ b/lib/lsg_irc/youtube_handler.ex
@@ -7,7 +7,7 @@ defmodule LSG.IRC.YouTubeHandler do
* **!yt `<recherche>`**, !youtube `<recherche>`: retourne le premier résultat de la `<recherche>` YouTube
"""
- defstruct client: nil, dets: nil
+ defstruct client: nil
def irc_doc, do: @moduledoc
diff --git a/lib/lsg_web/controllers/irc_controller.ex b/lib/lsg_web/controllers/irc_controller.ex
index bff5476..af7fff1 100644
--- a/lib/lsg_web/controllers/irc_controller.ex
+++ b/lib/lsg_web/controllers/irc_controller.ex
@@ -3,7 +3,7 @@ defmodule LSGWeb.IrcController do
def index(conn, _) do
doc = LSG.IRC.TxtHandler.irc_doc()
- commands = for mod <- Application.get_env(:lsg, :irc)[:handlers] do
+ commands = for mod <- (Application.get_env(:lsg, :irc)[:plugins] ++ Application.get_env(:lsg, :irc)[:handlers]) do
mod.irc_doc()
end
|> Enum.reject(fn(i) -> i == nil end)