Current section
Files
Jump to
Current section
Files
src/glupbit@websocket@connection.erl
-module(glupbit@websocket@connection).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glupbit/websocket/connection.gleam").
-export([subscribe/3, connect_public/2, connect_private/3]).
-export_type([ws_message/0, ws_command/0, ws_state/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" WebSocket connection management via stratus actors.\n"
"\n"
" Handles public and private (authenticated) connections with automatic\n"
" message routing based on the `type` field in incoming JSON.\n"
).
-type ws_message() :: {ticker_msg, glupbit@websocket@subscription:ticker_data()} |
{trade_msg, glupbit@websocket@subscription:trade_data()} |
{orderbook_msg, glupbit@websocket@subscription:orderbook_data()} |
{status_msg, binary()} |
{error_msg, binary(), binary()} |
{raw_msg, binary()}.
-type ws_command() :: {subscribe,
list(glupbit@websocket@subscription:subscription()),
glupbit@websocket@subscription:ws_format()}.
-type ws_state(ACEP) :: {ws_state, fun((ACEP, ws_message()) -> ACEP), ACEP}.
-file("src/glupbit/websocket/connection.gleam", 76).
?DOC(" Send a subscription request over the WebSocket.\n").
-spec subscribe(
gleam@erlang@process:subject(stratus:internal_message(ws_command())),
list(glupbit@websocket@subscription:subscription()),
glupbit@websocket@subscription:ws_format()
) -> nil.
subscribe(Conn, Subscriptions, Format) ->
gleam@erlang@process:send(
Conn,
stratus:to_user_message({subscribe, Subscriptions, Format})
).
-file("src/glupbit/websocket/connection.gleam", 146).
-spec status_decoder() -> gleam@dynamic@decode:decoder(binary()).
status_decoder() ->
gleam@dynamic@decode:field(
<<"status"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Status) -> gleam@dynamic@decode:success(Status) end
).
-file("src/glupbit/websocket/connection.gleam", 151).
-spec error_decoder() -> gleam@dynamic@decode:decoder({binary(), binary()}).
error_decoder() ->
gleam@dynamic@decode:subfield(
[<<"error"/utf8>>, <<"name"/utf8>>],
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Name) ->
gleam@dynamic@decode:subfield(
[<<"error"/utf8>>, <<"message"/utf8>>],
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Message) ->
gleam@dynamic@decode:success({Name, Message})
end
)
end
).
-file("src/glupbit/websocket/connection.gleam", 157).
-spec type_field_decoder() -> gleam@dynamic@decode:decoder(binary()).
type_field_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Type_name) -> gleam@dynamic@decode:success(Type_name) end
).
-file("src/glupbit/websocket/connection.gleam", 116).
-spec decode_ws_message(binary()) -> ws_message().
decode_ws_message(Text) ->
case gleam@json:parse(Text, status_decoder()) of
{ok, Status} ->
{status_msg, Status};
{error, _} ->
case gleam@json:parse(Text, error_decoder()) of
{ok, {Name, Message}} ->
{error_msg, Name, Message};
{error, _} ->
case gleam@json:parse(Text, type_field_decoder()) of
{ok, <<"ticker"/utf8>>} ->
case gleam@json:parse(
Text,
glupbit@websocket@subscription:ticker_data_decoder(
)
) of
{ok, Data} ->
{ticker_msg, Data};
{error, _} ->
{raw_msg, Text}
end;
{ok, <<"trade"/utf8>>} ->
case gleam@json:parse(
Text,
glupbit@websocket@subscription:trade_data_decoder(
)
) of
{ok, Data@1} ->
{trade_msg, Data@1};
{error, _} ->
{raw_msg, Text}
end;
{ok, <<"orderbook"/utf8>>} ->
case gleam@json:parse(
Text,
glupbit@websocket@subscription:orderbook_data_decoder(
)
) of
{ok, Data@2} ->
{orderbook_msg, Data@2};
{error, _} ->
{raw_msg, Text}
end;
{ok, _} ->
{raw_msg, Text};
{error, _} ->
{raw_msg, Text}
end
end
end.
-file("src/glupbit/websocket/connection.gleam", 86).
-spec start_ws(
gleam@http@request:request(binary()),
ACFA,
fun((ACFA, ws_message()) -> ACFA)
) -> {ok, gleam@erlang@process:subject(stratus:internal_message(ws_command()))} |
{error, stratus:initialization_error()}.
start_ws(Req, User_state, Handler) ->
Initial_state = {ws_state, Handler, User_state},
_pipe = stratus:new(Req, Initial_state),
_pipe@1 = stratus:on_message(_pipe, fun(State, Msg, Conn) -> case Msg of
{text, Text} ->
Ws_msg = decode_ws_message(Text),
New_user_state = (erlang:element(2, State))(
erlang:element(3, State),
Ws_msg
),
stratus:continue(
{ws_state, erlang:element(2, State), New_user_state}
);
{binary, _} ->
stratus:continue(State);
{user, {subscribe, Subs, Format}} ->
Ticket = youid@uuid:v4_string(),
Msg_text = glupbit@websocket@subscription:build_subscription_message(
Ticket,
Subs,
Format
),
_ = stratus:send_text_message(Conn, Msg_text),
stratus:continue(State)
end end),
_pipe@2 = stratus:on_close(_pipe@1, fun(_, _) -> nil end),
_pipe@3 = stratus:start(_pipe@2),
gleam@result:map(_pipe@3, fun(Started) -> erlang:element(3, Started) end).
-file("src/glupbit/websocket/connection.gleam", 55).
?DOC(" Connect to the public WebSocket.\n").
-spec connect_public(ACES, fun((ACES, ws_message()) -> ACES)) -> {ok,
gleam@erlang@process:subject(stratus:internal_message(ws_command()))} |
{error, stratus:initialization_error()}.
connect_public(User_state, Handler) ->
Req@1 = case gleam@http@request:to(
<<"wss://api.upbit.com/websocket/v1"/utf8>>
) of
{ok, Req} -> Req;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glupbit/websocket/connection"/utf8>>,
function => <<"connect_public"/utf8>>,
line => 59,
value => _assert_fail,
start => 1500,
'end' => 1546,
pattern_start => 1511,
pattern_end => 1518})
end,
start_ws(Req@1, User_state, Handler).
-file("src/glupbit/websocket/connection.gleam", 64).
?DOC(" Connect to the private WebSocket with authentication.\n").
-spec connect_private(
glupbit@auth:credentials(),
ACEV,
fun((ACEV, ws_message()) -> ACEV)
) -> {ok, gleam@erlang@process:subject(stratus:internal_message(ws_command()))} |
{error, stratus:initialization_error()}.
connect_private(Creds, User_state, Handler) ->
Req@1 = case gleam@http@request:to(
<<"wss://api.upbit.com/websocket/v1/private"/utf8>>
) of
{ok, Req} -> Req;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glupbit/websocket/connection"/utf8>>,
function => <<"connect_private"/utf8>>,
line => 69,
value => _assert_fail,
start => 1858,
'end' => 1905,
pattern_start => 1869,
pattern_end => 1876})
end,
Token@1 = case glupbit@auth:generate_token(Creds) of
{ok, Token} -> Token;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"glupbit/websocket/connection"/utf8>>,
function => <<"connect_private"/utf8>>,
line => 70,
value => _assert_fail@1,
start => 1908,
'end' => 1957,
pattern_start => 1919,
pattern_end => 1928})
end,
Req@2 = gleam@http@request:set_header(
Req@1,
<<"authorization"/utf8>>,
<<"Bearer "/utf8, Token@1/binary>>
),
start_ws(Req@2, User_state, Handler).