defmodule DeribitApi.API.WebSockets do @moduledoc """ Deribit WebSockets API. """ use Agent require Logger @api_v2 "/ws/api/v2" def child_spec(opts) do %{ id: __MODULE__, start: {__MODULE__, :start_link, [opts]}, type: :worker, restart: :permanent, shutdown: 500 } end def start_link do Agent.start_link( fn -> %{ "websocket" => Socket.Web.connect!(base_endpoint(), path: @api_v2, secure: true ) } end, name: __MODULE__ ) end def socket do Agent.get(__MODULE__, fn x -> Map.get(x, "websocket") end) end def obtain_result(text) do json = M.dec!(text) # I'm looking for a map with a result entry case Map.has_key?(json, "result") do true -> result = Map.get(json, "result") access = Map.has_key?(result, "access_token") refresh = Map.has_key?(result, "refresh_token") case access && refresh do true -> result end end nil end ####### Authentication section ########## private defp params_for_auth(params \\ %{}) do %{} |> Map.put("grant_type", "client_credentials") |> Map.put("client_id", Map.get(params, "client_id")) |> Map.put("client_secret", Map.get(params, "client_secret")) end ########## private defp payload_for_auth(cid, csk) do %{} |> Map.put("method", "public/auth") |> Map.put("params", params_for_auth(%{"client_id" => cid, "client_secret" => csk})) end def authenticate(client_id, client_secret) do # The response contains 'result' if all is well, and you can use the # access_token and refresh_token to authenticate subsequent requests. # we store the entire deserialized 'result' in this module's Agent. # # So far, the Agent is keeping track of just 2 things: # 1) the underlying tcp socket used to maintain the websocket connection # # 2) the 'result' payload from the initial response from '/public/auth' # this includes the following: # access_token # refresh_token # token_type "bearer" # scope account permissions # expires_in basically a TTL until access_token expires # # Now for every request until we need to re-authenticate (we know how long # we can use our access_token, from the expires_in TTL. new_m = Agent.get(__MODULE__, fn data -> websocket = Map.get(data, "websocket") case Socket.Web.send!( websocket, {:text, M.enc!(payload_for_auth(client_id, client_secret))} ) do :ok -> case Socket.Web.recv!(websocket) do {:text, text} -> dec_text = M.dec!(text) IO.inspect(dec_text) {:ok, Map.put(data, "result", obtain_result(text))} {:ping, _} -> {Socket.Web.send!(websocket, {:pong, ""}), data} abc -> IO.puts "This is abc..." IO.inspect(abc) end :error -> raise "Error!" x -> IO.puts "This is x..." IO.inspect(x) end end) {status, x} = new_m case status do :ok -> Agent.update(__MODULE__, fn m -> x end) _y -> raise "no new map: #{_y}" end end def base_method(mode, url, params \\ %{}) do case mode do :public -> pub = %{ "method" => "public/" <> url, "params" => params } pub :private -> prv = %{ "method" => "private/" <> url, "params" => params } prv end end # def ws, do: Agent.get(__MODULE__, fn data -> Map.get(data, "websocket") end) @spec get_public(any, any, any) :: any def get_public(url, params \\ %{}, f) do Agent.get(__MODULE__, fn data -> IO.inspect(data) websocket = Map.get(data, "websocket") IO.inspect(params) case Socket.Web.send!(websocket, {:text, M.enc!(base_method(:public, url, params))}) do :ok -> case Socket.Web.recv!(websocket) do {:text, text} -> parsed_text = M.dec!(text) f.(parsed_text) {:ok, spawn(DeribitApi.Listener, :listen, [websocket, data, f])} {:ping, _} -> Socket.Web.send!(websocket, {:pong, ""}) other -> IO.inspect(other) end end end) end def get_private(url, client_id, client_secret, params \\ %{}, f) do Agent.get(__MODULE__, fn data -> access_token = data |> Map.get("result") |> Map.get("access_token") websocket = Map.get(data, "websocket") params = Map.put(params, "access_token", access_token) case Socket.Web.send!(websocket, {:text, M.enc!(base_method(:private, url, params))}) do :ok -> case Socket.Web.recv!(websocket) do {:text, text} -> parsed_text = M.dec!(text) f.(parsed_text) {:ok, parsed_text} {:ping, _} -> Socket.Web.send!(websocket, {:pong, ""}) end end end) end defp get_timestamp do DateTime.utc_now() |> DateTime.to_unix() |> Kernel.*(1000) end def base_endpoint do case Application.get_env(:deribit_api, :test) do false -> Application.get_env(:deribit_api, :base_url) true -> Application.get_env(:deribit_api, :test_url) end end end