defmodule LSG.IRC.LastFmHandler do
require Logger
@moduledoc """
# last.fm
* **!lastfm `[nick|username]`**
* **!lastfmall**
* **+lastfm `<username last.fm>`, -lastfm**
"""
defstruct client: nil, dets: nil
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())
dets_filename = (LSG.data_path() <> "/" <> "lastfm.dets") |> String.to_charlist
{:ok, dets} = :dets.open_file(dets_filename, [])
{:ok, %__MODULE__{client: client, dets: dets}}
end
def handle_info({:received, "+lastfm " <> username, sender, chan}, state) do
username = String.strip(username)
:ok = :dets.insert(state.dets, {String.downcase(sender.nick), username})
ExIRC.Client.msg(state.client, :privmsg, chan, "#{sender.nick}: nom d'utilisateur last.fm configuré: \"#{username}\"")
{:noreply, state}
end
def handle_info({:received, "-lastfm", sender, chan}, state) do
text = case :dets.lookup(state.dets, sender.nick) do
[{_nick, username}] ->
:dets.delete(state.dets, String.downcase(sender.nick))
"#{sender.nick}: nom d'utilisateur last.fm enlevé"
_ -> ""
end
ExIRC.Client.msg(state.client, :privmsg, chan, text)
{:noreply, state}
end
def handle_info({:received, "!lastfm", sender, chan}, state) do
irc_now_playing(sender.nick, chan, state)
{:noreply, state}
end
def handle_info({:received, "!lastfm " <> nick_or_user, sender, chan}, state) do
irc_now_playing(nick_or_user, chan, state)
{:noreply, state}
end
def handle_info({:received, "!lastfmall", sender, chan}, state) do
foldfun = fn({_nick, user}, acc) -> [user|acc] end
usernames = :dets.foldl(foldfun, [], state.dets)
|> Enum.uniq
for u <- usernames, do: irc_now_playing(u, chan, 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, chan, state) do
nick_or_user = String.strip(nick_or_user)
username = case :dets.lookup(state.dets, String.downcase(nick_or_user)) do
[{^nick_or_user, username}] -> username
_ -> nick_or_user
end
case now_playing(username) do
{:error, text} when is_binary(text) -> ExIRC.Client.msg(state.client, :privmsg, chan, text)
{:ok, map} when is_map(map) ->
text = format_now_playing(map)
user = lookup_nick(username, state)
if user && text, do: ExIRC.Client.msg(state.client, :privmsg, chan, "#{user} #{text}")
other ->
IO.inspect(other)
nil
end
end
defp now_playing(user) do
api = Application.get_env(:lsg, __MODULE__)[:api_key]
url = "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=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: 400}} -> {: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 format_now_playing(%{"recenttracks" => %{"track" => [track = %{"@attr" => %{"nowplaying" => "true"}}, _old]}}) do
format_track(true, track)
end
defp format_now_playing(%{"recenttracks" => %{"track" => [track]}}) do
format_track(false, track)
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) do
album = if track["album"]["#text"] do
" (" <> track["album"]["#text"] <> ")"
else
""
end
action = if np, do: "écoute ", else: "a écouté "
action <> track["artist"]["#text"] <> " - " <> track["name"] <> album <> " — " <> track["url"]
end
defp lookup_nick(username, state) do
case :dets.match(state.dets, {:'$1', username}) do
[[match]] -> match
[[match] | _many] -> match
_ -> username
end
end
end