diff options
Diffstat (limited to 'lib')
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 47ed792..4b02dc2 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) |