summaryrefslogtreecommitdiff
path: root/lib/plugins/last_fm.ex
diff options
context:
space:
mode:
authorJordan Bracco <href@random.sh>2022-12-20 02:19:42 +0000
committerJordan Bracco <href@random.sh>2022-12-20 19:29:41 +0100
commit9958e90eb5eb5a2cc171c40860745e95a96bd429 (patch)
treeb49cdb1d0041b9c0a81a14950d38c0203896f527 /lib/plugins/last_fm.ex
parentRename to Nola (diff)
Actually do not prefix folders with nola_ refs T77
Diffstat (limited to 'lib/plugins/last_fm.ex')
-rw-r--r--lib/plugins/last_fm.ex187
1 files changed, 187 insertions, 0 deletions
diff --git a/lib/plugins/last_fm.ex b/lib/plugins/last_fm.ex
new file mode 100644
index 0000000..1a9b7dd
--- /dev/null
+++ b/lib/plugins/last_fm.ex
@@ -0,0 +1,187 @@
+defmodule Nola.Plugins.LastFm do
+ require Logger
+
+ @moduledoc """
+ # last.fm
+
+ * **!lastfm|np `[nick|username]`**
+ * **.lastfm|np**
+ * **+lastfm, -lastfm `<username last.fm>; ?lastfm`** Configurer un nom d'utilisateur last.fm
+ """
+
+ @single_trigger ~w(lastfm np)
+ @pubsub_topics ~w(trigger:lastfm trigger:np)
+
+ defstruct dets: nil
+
+ def irc_doc, do: @moduledoc
+
+ def start_link() do
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
+ end
+
+ def init([]) do
+ regopts = [type: __MODULE__]
+ for t <- @pubsub_topics, do: {:ok, _} = Registry.register(IRC.PubSub, t, type: __MODULE__)
+ 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
+ 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}\".")
+ {: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
+ {: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
+ {:noreply, state}
+ end
+
+ def handle_info({:irc, :trigger, _, message = %{trigger: %{type: :bang, args: []}}}, state) do
+ irc_now_playing(message.account.id, message, state)
+ {:noreply, state}
+ end
+
+ 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 = IRC.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)
+ for u <- usernames, do: irc_now_playing(u, message, state)
+ {:noreply, state}
+ end
+
+ def handle_info(info, state) 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, message, state) do
+ nick_or_user = String.strip(nick_or_user)
+
+ id_or_user = if account = IRC.Account.get(nick_or_user) || IRC.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 = IRC.Account.get(id_or_user) do
+ user = IRC.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
+ end
+
+ 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
+ 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)}"}
+ error ->
+ Logger.error "Lastfm http error: #{inspect error}"
+ :error
+ end
+ end
+ 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)
+ 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}"
+ :error
+ end
+ end
+
+ defp format_now_playing(%{"recenttracks" => %{"track" => [track = %{"@attr" => %{"nowplaying" => "true"}} | _]}}, et) do
+ format_track(true, track, et)
+ end
+
+ defp format_now_playing(%{"recenttracks" => %{"track" => [track | _]}}, et) do
+ format_track(false, track, et)
+ end
+
+ defp format_now_playing(%{"error" => err, "message" => message}, _) do
+ "last.fm error #{err}: #{message}"
+ end
+
+ defp format_now_playing(miss) do
+ nil
+ end
+
+ defp format_track(np, track, extended) do
+ artist = track["artist"]["name"]
+ album = if track["album"]["#text"], do: " (" <> track["album"]["#text"] <> ")", else: ""
+ name = track["name"] <> album
+ 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(", ")
+
+ [action, artist, name, count, tags, track["url"]]
+ |> Enum.filter(& &1)
+ |> Enum.map(&String.trim(&1))
+ |> Enum.join(" - ")
+ end
+
+end