Current section
Files
Jump to
Current section
Files
src/shimmer@ws@event_loop.erl
-module(shimmer@ws@event_loop).
-compile(no_auto_import).
-export([actor_setup/4, actor_loop/2]).
-export_type([message/0, websocket_meta/0, gateway_session/0, sharding_meta/0, actor_state/0]).
-type message() :: {websocket_frame, shimmer@internal@network@websocket:frame()} |
{update_presence, shimmer@types@presence:presence()} |
beat |
halt.
-type websocket_meta() :: {websocket_meta,
binary(),
integer(),
shimmer@handlers:handlers(message())}.
-type gateway_session() :: {gateway_session, binary(), binary()}.
-type sharding_meta() :: {sharding_meta,
gleam@erlang@process:subject(message()),
integer(),
integer()}.
-type actor_state() :: {actor_state,
integer(),
integer(),
shimmer@internal@network@websocket:connection(),
websocket_meta(),
sharding_meta(),
gleam@erlang@process:selector(message()),
gleam@erlang@process:subject(message()),
gleam@option:option(gateway_session())}.
-spec actor_setup(
shimmer@client:client(message()),
binary(),
shimmer@handlers:handlers(message()),
gleam@option:option(sharding_meta())
) -> fun(() -> gleam@otp@actor:init_result(actor_state(), message())).
actor_setup(Client, Gateway_url, Handlers, Sharding) ->
fun() ->
Setup = fun(Inner_client) ->
Url = uri_ffi:parse(Gateway_url),
case begin
_pipe = shimmer@internal@network@websocket:connect(
erlang:element(2, Url),
<<"/?v=10&encoding=etf"/utf8>>,
443,
[]
),
gleam@result:replace_error(
_pipe,
{failed, <<"Failed to open websocket"/utf8>>}
)
end of
{error, _try} -> {error, _try};
{ok, Conn} ->
To_self_subject = gleam@erlang@process:new_subject(),
Selector = begin
_pipe@1 = gleam_erlang_ffi:new_selector(),
_pipe@2 = gleam@erlang@process:selecting(
_pipe@1,
erlang:element(4, Inner_client),
fun(A) -> A end
),
_pipe@3 = gleam@erlang@process:selecting(
_pipe@2,
To_self_subject,
fun(A@1) -> A@1 end
),
gleam@erlang@process:selecting_record4(
_pipe@3,
erlang:binary_to_atom(<<"gun_ws"/utf8>>),
fun(_, _, Dyn_frame) ->
Map = fun(Frame) -> {websocket_frame, Frame} end,
Map(gleam@dynamic:unsafe_coerce(Dyn_frame))
end
)
end,
{ok,
{ready,
{actor_state,
-1,
-1,
Conn,
{websocket_meta,
erlang:element(2, Inner_client),
erlang:element(3, Inner_client),
Handlers},
begin
_pipe@4 = Sharding,
gleam@option:unwrap(
_pipe@4,
{sharding_meta, To_self_subject, 1, 1}
)
end,
Selector,
To_self_subject,
none},
Selector}}
end
end,
case Setup(Client) of
{ok, Ready} ->
Ready;
{error, Failed} ->
Failed
end
end.
-spec internal_error_handler(actor_state(), {ok, any()} | {error, any()}) -> gleam@otp@actor:next(actor_state()).
internal_error_handler(State, _) ->
{continue, State}.
-spec actor_loop(message(), actor_state()) -> gleam@otp@actor:next(actor_state()).
actor_loop(Msg, State) ->
case Msg of
{websocket_frame, {binary, Etf_bitstring}} ->
Dynamic_payload = shimmer_ws:parse_etf(Etf_bitstring),
case shimmer@ws@packet:from_dynamic(Dynamic_payload) of
{ok, {0, Seq, {some, <<"READY"/utf8>>}, {some, Data}}} ->
case shimmer@ws@packets@ready:from_map(Data) of
{ok, Packet} ->
(erlang:element(
2,
erlang:element(4, erlang:element(5, State))
))(
Packet,
{client,
erlang:element(2, erlang:element(5, State)),
erlang:element(3, erlang:element(5, State)),
erlang:element(8, State)}
),
{continue,
erlang:setelement(
9,
update_state(Seq, State),
{some,
{gateway_session,
erlang:element(3, Packet),
erlang:element(4, Packet)}}
)};
{error, E} ->
internal_error_handler(
update_state(Seq, State),
{error, E}
)
end;
{ok,
{0,
Seq@1,
{some, <<"MESSAGE_CREATE"/utf8>>},
{some, Data@1}}} ->
case shimmer@ws@packets@message_create:from_map(Data@1) of
{ok, Packet@1} ->
(erlang:element(
3,
erlang:element(4, erlang:element(5, State))
))(
Packet@1,
{client,
erlang:element(2, erlang:element(5, State)),
erlang:element(3, erlang:element(5, State)),
erlang:element(8, State)}
),
{continue, update_state(Seq@1, State)};
{error, E@1} ->
internal_error_handler(
update_state(Seq@1, State),
{error, E@1}
)
end;
{ok, {10, Seq@2, _@1, {some, Data@2}}} ->
case shimmer@ws@packets@hello:from_map(Data@2) of
{ok, Packet@2} ->
New_state = erlang:setelement(
2,
update_state(Seq@2, State),
erlang:element(2, Packet@2)
),
shimmer@internal@network@websocket:send(
erlang:element(4, New_state),
begin
_pipe = gleam@map:new(),
_pipe@1 = gleam@map:insert(
_pipe,
<<"op"/utf8>>,
gleam@dynamic:from(1)
),
_pipe@2 = gleam@map:insert(
_pipe@1,
<<"d"/utf8>>,
gleam@dynamic:from(nil)
),
erlang:term_to_binary(_pipe@2)
end
),
shimmer@internal@network@websocket:send(
erlang:element(4, New_state),
begin
_pipe@3 = {identify_packet_data,
erlang:element(
2,
erlang:element(5, State)
),
erlang:element(
3,
erlang:element(5, State)
),
erlang:element(
3,
erlang:element(6, State)
),
erlang:element(
4,
erlang:element(6, State)
)},
shimmer@ws@packets@identify:to_etf(_pipe@3)
end
),
gleam@erlang@process:send_after(
erlang:element(8, New_state),
erlang:element(2, New_state),
beat
),
{continue, New_state};
{error, E@2} ->
internal_error_handler(
update_state(Seq@2, State),
{error, E@2}
)
end;
{ok, {11, Seq@3, _@2, _@3}} ->
(erlang:element(
4,
erlang:element(4, erlang:element(5, State))
))(
{client,
erlang:element(2, erlang:element(5, State)),
erlang:element(3, erlang:element(5, State)),
erlang:element(8, State)}
),
{continue, update_state(Seq@3, State)};
{ok, {_@4, Seq@4, _@5, _@6}} ->
{continue, update_state(Seq@4, State)};
{error, E@3} ->
internal_error_handler(State, {error, E@3});
_@7 ->
{continue, State}
end;
{websocket_frame, {close, Code, Message}} ->
gleam@io:println(
begin
_pipe@4 = [<<"Websocket Closed with code: "/utf8>>,
gleam@int:to_string(Code),
<<" and message \""/utf8>>,
Message,
<<"\""/utf8>>],
gleam@string:join(_pipe@4, <<""/utf8>>)
end
),
(erlang:element(5, erlang:element(4, erlang:element(5, State))))(
Code,
{client,
erlang:element(2, erlang:element(5, State)),
erlang:element(3, erlang:element(5, State)),
erlang:element(8, State)}
),
{stop, {abnormal, Message}};
{update_presence, New_presence} ->
Payload = begin
_pipe@5 = gleam@map:new(),
_pipe@6 = gleam@map:insert(
_pipe@5,
<<"op"/utf8>>,
gleam@dynamic:from(3)
),
_pipe@9 = gleam@map:insert(
_pipe@6,
<<"d"/utf8>>,
begin
_pipe@7 = New_presence,
_pipe@8 = shimmer@types@presence:to_map(_pipe@7),
gleam@dynamic:from(_pipe@8)
end
),
erlang:term_to_binary(_pipe@9)
end,
shimmer@internal@network@websocket:send(
erlang:element(4, State),
Payload
),
{continue, State};
beat ->
Payload@1 = begin
_pipe@14 = case erlang:element(3, State) of
-1 ->
_pipe@10 = gleam@map:new(),
_pipe@11 = gleam@map:insert(
_pipe@10,
<<"op"/utf8>>,
gleam@dynamic:from(1)
),
gleam@map:insert(
_pipe@11,
<<"d"/utf8>>,
gleam@dynamic:from(nil)
);
Seq@5 ->
_pipe@12 = gleam@map:new(),
_pipe@13 = gleam@map:insert(
_pipe@12,
<<"op"/utf8>>,
gleam@dynamic:from(1)
),
gleam@map:insert(
_pipe@13,
<<"d"/utf8>>,
gleam@dynamic:from(gleam@int:to_string(Seq@5))
)
end,
erlang:term_to_binary(_pipe@14)
end,
shimmer@internal@network@websocket:send(
erlang:element(4, State),
Payload@1
),
gleam@erlang@process:send_after(
erlang:element(8, State),
erlang:element(2, State),
beat
),
{continue, State};
_@8 ->
{stop,
{abnormal,
<<"event loop's actor recieved unknown message"/utf8>>}}
end.
-spec update_state(gleam@option:option(integer()), actor_state()) -> actor_state().
update_state(Seq, Old_state) ->
erlang:setelement(
3,
Old_state,
begin
_pipe = Seq,
gleam@option:unwrap(_pipe, erlang:element(3, Old_state))
end
).