diff --git a/lib/plugins/finance.ex b/lib/plugins/finance.ex index 3ecc2fb..b083df8 100644 --- a/lib/plugins/finance.ex +++ b/lib/plugins/finance.ex @@ -1,190 +1,199 @@ defmodule Nola.Plugins.Finance do require Logger @moduledoc """ # finance Données de [alphavantage.co](https://alphavantage.co). ## forex / monnaies / crypto-monnaies * **`!forex [MONNAIE2]`**: taux de change entre deux monnaies. * **`!forex `**: converti `montant` entre deux monnaies * **`?currency `**: recherche une monnaie Utiliser le symbole des monnaies (EUR, USD, ...). ## bourses * **`!stocks `** * **`?stocks `** cherche un symbole Pour les symboles non-US, ajouter le suffixe (RNO Paris: RNO.PAR). """ @currency_list "http://www.alphavantage.co/physical_currency_list/" @crypto_list "http://www.alphavantage.co/digital_currency_list/" HTTPoison.start() 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) {symbol, name} end) |> 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 def init([]) do regopts = [plugin: __MODULE__] {:ok, _} = Registry.register(Nola.PubSub, "trigger:forex", regopts) {:ok, _} = Registry.register(Nola.PubSub, "trigger:currency", regopts) {:ok, _} = Registry.register(Nola.PubSub, "trigger:stocks", regopts) {:ok, nil} end 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()}" 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}") 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 message.replyfun.(items) end {:ok, resp = %HTTPoison.Response{status_code: code}} -> 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})") end - {:noreply, state} 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()}" 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}") - message.replyfun.("stocks: requête invalide") - else - data = Map.get(data, "Global Quote") - open = Map.get(data, "02. open") - high = Map.get(data, "03. high") - low = Map.get(data, "04. low") - price = Map.get(data, "05. price") - volume = Map.get(data, "06. volume") - prev_close = Map.get(data, "08. previous close") - 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})" - message.replyfun.(msg) + IO.inspect(data) + case data do + %{"Error Message" => 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") + low = Map.get(data, "04. low") + price = Map.get(data, "05. price") + volume = Map.get(data, "06. volume") + prev_close = Map.get(data, "08. previous close") + 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})" + 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}" 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})") 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()}" 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}") message.replyfun.("forex: requête invalide") else data = Map.get(data, "Realtime Currency Exchange Rate") from_name = Map.get(data, "2. From_Currency Name") 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})") end {:ok, resp = %HTTPoison.Response{status_code: code}} -> 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})") end {:noreply, state} end 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(", ") if results == "" do message.replyfun.("no results!") else message.replyfun.(results) end {:noreply, state} end defp api_key() do Application.get_env(:nola, :alphavantage, []) |> Keyword.get(:api_key, "demo") end end