Current section
Files
Jump to
Current section
Files
src/discord_gleam@ws@event_loop.erl
-module(discord_gleam@ws@event_loop).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([main/6]).
-export_type([state/0]).
-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(
" Event loop for handling the discord gateway websocket\n"
" Dispatches events to registered event handlers\n"
).
-type state() :: {state, boolean(), integer()}.
-file("src/discord_gleam/ws/event_loop.gleam", 31).
?DOC(" Start the event loop, with a set of event handlers.\n").
-spec main(
discord_gleam@types@bot:bot(),
list(fun((discord_gleam@types@bot:bot(), discord_gleam@event_handler:packet()) -> nil)),
binary(),
boolean(),
binary(),
bravo@uset:u_set({binary(), binary()})
) -> nil.
main(Bot, Event_handlers, Host, Reconnect, Session_id, State_uset) ->
logging:log(debug, <<"Requesting gateway"/utf8>>),
bravo@uset:insert(State_uset, [{<<"sequence"/utf8>>, <<"0"/utf8>>}]),
Host@1 = gleam@string:replace(Host, <<"wss://"/utf8>>, <<""/utf8>>),
Req = begin
_pipe = gleam@http@request:new(),
_pipe@1 = gleam@http@request:set_host(_pipe, Host@1),
_pipe@2 = gleam@http@request:set_scheme(_pipe@1, https),
_pipe@3 = gleam@http@request:set_path(
_pipe@2,
<<"/?v=10&encoding=json"/utf8>>
),
_pipe@4 = gleam@http@request:set_header(
_pipe@3,
<<"User-Agent"/utf8>>,
<<"DiscordBot (https://github.com/cyteon/discord_gleam, 1.2.0)"/utf8>>
),
_pipe@5 = gleam@http@request:set_header(
_pipe@4,
<<"Host"/utf8>>,
<<"gateway.discord.gg"/utf8>>
),
_pipe@6 = gleam@http@request:set_header(
_pipe@5,
<<"Connection"/utf8>>,
<<"Upgrade"/utf8>>
),
_pipe@7 = gleam@http@request:set_header(
_pipe@6,
<<"Upgrade"/utf8>>,
<<"websocket"/utf8>>
),
gleam@http@request:set_header(
_pipe@7,
<<"Sec-WebSocket-Version"/utf8>>,
<<"13"/utf8>>
)
end,
logging:log(debug, <<"Creating builder"/utf8>>),
Initial_state = {state, false, 0},
Last_connect = birl:now(),
Builder = begin
_pipe@9 = stratus:websocket(
Req,
fun() ->
logging:log(debug, <<"Builder init"/utf8>>),
{Initial_state, none}
end,
fun(Msg, State, Conn) -> case Msg of
{text, Msg@1} ->
logging:log(
debug,
<<"Gateway text msg: "/utf8, Msg@1/binary>>
),
case erlang:element(2, State) of
false ->
Identify = case Reconnect of
true ->
discord_gleam@ws@packets@identify:create_resume_packet(
erlang:element(2, Bot),
erlang:element(4, Bot),
Session_id,
case bravo@uset:lookup(
State_uset,
<<"sequence"/utf8>>
) of
{ok, S} ->
erlang:element(2, S);
{error, _} ->
<<"0"/utf8>>
end
);
false ->
discord_gleam@ws@packets@identify:create_packet(
erlang:element(2, Bot),
erlang:element(4, Bot)
)
end,
_ = stratus:send_text_message(Conn, Identify),
New_state = {state, true, 0},
Heartbeat = discord_gleam@ws@packets@hello:string_to_data(
Msg@1
),
gleam@erlang@process:start(
fun() ->
repeatedly_ffi:call(
Heartbeat,
nil,
fun(_, _) ->
S@2 = case bravo@uset:lookup(
State_uset,
<<"sequence"/utf8>>
) of
{ok, S@1} ->
case gleam_stdlib:parse_int(
erlang:element(
2,
S@1
)
) of
{ok, I} ->
I;
{error, _} ->
0
end;
{error, _} ->
0
end,
Packet = begin
_pipe@8 = gleam@json:object(
[{<<"op"/utf8>>,
gleam@json:int(
1
)},
{<<"d"/utf8>>,
gleam@json:string(
<<"null"/utf8>>
)},
{<<"s"/utf8>>,
gleam@json:int(
S@2
)}]
),
gleam@json:to_string(
_pipe@8
)
end,
logging:log(
debug,
<<"Sending heartbeat: "/utf8,
Packet/binary>>
),
stratus:send_text_message(
Conn,
Packet
)
end
)
end,
false
),
gleam@otp@actor:continue(New_state);
true ->
Generic_packet = discord_gleam@ws@packets@generic:string_to_data(
Msg@1
),
case erlang:element(3, Generic_packet) of
0 ->
nil;
_ ->
bravo@uset:insert(
State_uset,
[{<<"sequence"/utf8>>,
erlang:integer_to_binary(
erlang:element(
3,
Generic_packet
)
)}]
),
nil
end,
case erlang:element(4, Generic_packet) of
7 ->
logging:log(
debug,
<<"Received a reconnect request"/utf8>>
),
case stratus:close(Conn) of
{ok, _} ->
logging:log(
debug,
<<"Closed websocket"/utf8>>
);
{error, _} ->
logging:log(
error,
<<"Failed to close websocket"/utf8>>
)
end,
main(
Bot,
Event_handlers,
case bravo@uset:lookup(
State_uset,
<<"resume_gateway_url"/utf8>>
) of
{ok, Url} ->
erlang:element(2, Url);
{error, _} ->
<<"gateway.discord.gg"/utf8>>
end,
Reconnect,
case bravo@uset:lookup(
State_uset,
<<"session_id"/utf8>>
) of
{ok, S@3} ->
erlang:element(2, S@3);
{error, _} ->
<<""/utf8>>
end,
State_uset
);
_ ->
nil
end,
New_state@1 = {state,
true,
erlang:element(3, Generic_packet)},
discord_gleam@event_handler:handle_event(
Bot,
Msg@1,
Event_handlers,
State_uset
),
gleam@otp@actor:continue(New_state@1)
end;
{user, Msg@2} ->
logging:log(
debug,
<<"Gateway user msg: "/utf8, Msg@2/binary>>
),
gleam@otp@actor:continue(State);
{binary, _} ->
logging:log(debug, <<"Binary message"/utf8>>),
gleam@otp@actor:continue(State)
end end
),
stratus:on_close(
_pipe@9,
fun(_) ->
logging:log(debug, <<"The webhook was closed"/utf8>>),
Diff = birl:difference(Last_connect, birl:now()),
case birl@duration:compare(Diff, birl@duration:minutes(1)) of
gt ->
logging:log(
debug,
<<"Over 1 minute since connection, reconnecting"/utf8>>
),
main(
Bot,
Event_handlers,
case bravo@uset:lookup(
State_uset,
<<"resume_gateway_url"/utf8>>
) of
{ok, Url@1} ->
erlang:element(2, Url@1);
{error, _} ->
<<"gateway.discord.gg"/utf8>>
end,
Reconnect,
case bravo@uset:lookup(
State_uset,
<<"session_id"/utf8>>
) of
{ok, S@4} ->
erlang:element(2, S@4);
{error, _} ->
<<""/utf8>>
end,
State_uset
);
_ ->
logging:log(
error,
<<"Disconnected after too short time, not reconnecting"/utf8>>
),
nil
end,
nil
end
)
end,
_assert_subject = stratus:initialize(Builder),
{ok, Subj} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"discord_gleam/ws/event_loop"/utf8>>,
function => <<"main"/utf8>>,
line => 236})
end,
_pipe@10 = gleam_erlang_ffi:new_selector(),
_pipe@11 = gleam@erlang@process:selecting_process_down(
_pipe@10,
gleam@erlang@process:monitor_process(
gleam@erlang@process:subject_owner(Subj)
),
fun gleam@function:identity/1
),
gleam_erlang_ffi:select(_pipe@11),
logging:log(error, <<"websocket go bye bye"/utf8>>),
gleam_erlang_ffi:sleep(1000).