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, inline]).
-define(FILEPATH, "src/discord_gleam/ws/event_loop.gleam").
-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", 28).
?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(),
booklet:booklet(gleam@dict:dict(binary(), binary()))
) -> nil.
main(Bot, Event_handlers, Host, Reconnect, Session_id, State_ets) ->
logging:log(debug, <<"Requesting gateway"/utf8>>),
booklet_ffi:update(
State_ets,
fun(Cache) ->
gleam@dict:insert(Cache, <<"sequence"/utf8>>, <<"0"/utf8>>)
end
),
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.4.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},
Builder = begin
_pipe@8 = stratus:new(Req, Initial_state),
_pipe@10 = stratus:on_message(
_pipe@8,
fun(State, Msg, 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 gleam_stdlib:map_get(
booklet_ffi:get(State_ets),
<<"sequence"/utf8>>
) of
{ok, S} ->
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},
case discord_gleam@ws@packets@hello:string_to_data(
Msg@1
) of
{ok, Data} ->
proc_lib:spawn_link(
fun() ->
repeatedly_ffi:call(
erlang:element(
2,
erlang:element(3, Data)
),
nil,
fun(_, _) ->
S@2 = case gleam_stdlib:map_get(
booklet_ffi:get(
State_ets
),
<<"sequence"/utf8>>
) of
{ok, S@1} ->
case gleam_stdlib:parse_int(
S@1
) of
{ok, I} ->
I;
{error, _} ->
0
end;
{error, _} ->
0
end,
Packet = begin
_pipe@9 = gleam@json:object(
[{<<"op"/utf8>>,
gleam@json:int(
1
)},
{<<"d"/utf8>>,
case S@2 of
0 ->
gleam@json:null(
);
_ ->
gleam@json:int(
S@2
)
end}]
),
gleam@json:to_string(
_pipe@9
)
end,
logging:log(
debug,
<<"Sending heartbeat: "/utf8,
Packet/binary>>
),
_ = stratus:send_text_message(
Conn,
Packet
),
nil
end
)
end
),
nil;
{error, Err} ->
logging:log(
critical,
<<"Failed to decode hello packet: "/utf8,
(discord_gleam@internal@error:json_decode_error_to_string(
Err
))/binary>>
),
_ = stratus:close(Conn, {normal, <<>>}),
logging:log(
critical,
<<"Closing websocket due to fatal error"/utf8>>
)
end,
stratus: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;
_ ->
booklet_ffi:update(
State_ets,
fun(Cache@1) ->
gleam@dict:insert(
Cache@1,
<<"sequence"/utf8>>,
case gleam_stdlib:map_get(
booklet_ffi:get(
State_ets
),
<<"sequence"/utf8>>
) of
{ok, S@3} ->
S@3;
{error, _} ->
<<"0"/utf8>>
end
)
end
),
nil
end,
case erlang:element(4, Generic_packet) of
7 ->
logging:log(
debug,
<<"Received a reconnect request"/utf8>>
),
case stratus:close_custom(
Conn,
4009,
<<>>
) of
{ok, _} ->
logging:log(
debug,
<<"Closed websocket"/utf8>>
);
{error, _} ->
logging:log(
error,
<<"Failed to close websocket"/utf8>>
)
end,
main(
Bot,
Event_handlers,
case gleam_stdlib:map_get(
booklet_ffi:get(State_ets),
<<"resume_gateway_url"/utf8>>
) of
{ok, Url} ->
Url;
{error, _} ->
<<"gateway.discord.gg"/utf8>>
end,
Reconnect,
case gleam_stdlib:map_get(
booklet_ffi:get(State_ets),
<<"session_id"/utf8>>
) of
{ok, S@4} ->
S@4;
{error, _} ->
<<""/utf8>>
end,
State_ets
);
_ ->
nil
end,
New_state@1 = {state,
true,
erlang:element(3, Generic_packet)},
discord_gleam@event_handler:handle_event(
Bot,
Msg@1,
Event_handlers,
State_ets
),
stratus:continue(New_state@1)
end;
{user, Msg@2} ->
logging:log(
debug,
<<"Gateway user msg: "/utf8, Msg@2/binary>>
),
stratus:continue(State);
{binary, _} ->
logging:log(debug, <<"Binary message"/utf8>>),
stratus:continue(State)
end end
),
stratus:on_close(
_pipe@10,
fun(_, Close_reason) ->
logging:log(debug, <<"The webhook was closed"/utf8>>),
case Close_reason of
{custom, Custom_close_reason} ->
case stratus:get_custom_code(Custom_close_reason) of
4000 ->
logging:log(
error,
<<"Unknown error, not reconnecting"/utf8>>
);
4001 ->
logging:log(
error,
<<"Unknown opcode, not reconnecting"/utf8>>
);
4002 ->
logging:log(
error,
<<"Decode error, open a github issue, not reconnecting"/utf8>>
);
4003 ->
logging:log(
error,
<<"Not authenticated, not reconnecting"/utf8>>
);
4004 ->
logging:log(
error,
<<"Authentication failed, check your token, not reconnecting"/utf8>>
);
4005 ->
logging:log(
error,
<<"Already authenticated, open a github issue, not reconnecting"/utf8>>
);
4007 ->
logging:log(
error,
<<"Invalid sequence, reconnecting"/utf8>>
),
main(
Bot,
Event_handlers,
case gleam_stdlib:map_get(
booklet_ffi:get(State_ets),
<<"resume_gateway_url"/utf8>>
) of
{ok, Url@1} ->
Url@1;
{error, _} ->
<<"gateway.discord.gg"/utf8>>
end,
Reconnect,
<<""/utf8>>,
State_ets
);
4008 ->
logging:log(
error,
<<"You have been ratelimited, not reconnecting"/utf8>>
);
4009 ->
logging:log(
error,
<<"Session timed out, reconnecting"/utf8>>
),
main(
Bot,
Event_handlers,
case gleam_stdlib:map_get(
booklet_ffi:get(State_ets),
<<"resume_gateway_url"/utf8>>
) of
{ok, Url@2} ->
Url@2;
{error, _} ->
<<"gateway.discord.gg"/utf8>>
end,
Reconnect,
case gleam_stdlib:map_get(
booklet_ffi:get(State_ets),
<<"session_id"/utf8>>
) of
{ok, S@5} ->
S@5;
{error, _} ->
<<""/utf8>>
end,
State_ets
);
4010 ->
logging:log(
error,
<<"Invalid shard, not reconnecting"/utf8>>
);
4011 ->
logging:log(
error,
<<"Sharding required, not reconnecting"/utf8>>
);
4012 ->
logging:log(
error,
<<"Invalid API version, open a github issue, not reconnecting"/utf8>>
);
4013 ->
logging:log(
error,
<<"Invalid intents used, open a github issue as this should not happen, not reconnecting"/utf8>>
);
4014 ->
logging:log(
error,
<<"Disallowed intents used, did you remember to enable any priveleged intents you used in the discord developer portal (https://discord.dev)? Not reconnecting"/utf8>>
);
_ ->
logging:log(
error,
<<"Unknown close code, not reconnecting"/utf8>>
)
end,
nil;
_ ->
nil
end,
nil
end
)
end,
Actor@1 = case stratus:start(Builder) of
{ok, Actor} -> Actor;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"discord_gleam/ws/event_loop"/utf8>>,
function => <<"main"/utf8>>,
line => 346,
value => _assert_fail,
start => 10237,
'end' => 10282,
pattern_start => 10248,
pattern_end => 10257})
end,
Pid@1 = case gleam@erlang@process:subject_owner(erlang:element(3, Actor@1)) of
{ok, Pid} -> Pid;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"discord_gleam/ws/event_loop"/utf8>>,
function => <<"main"/utf8>>,
line => 347,
value => _assert_fail@1,
start => 10285,
'end' => 10339,
pattern_start => 10296,
pattern_end => 10303})
end,
Monitor = gleam@erlang@process:monitor(Pid@1),
Selector = gleam_erlang_ffi:new_selector(),
Selector@1 = gleam@erlang@process:select_specific_monitor(
Selector,
Monitor,
fun gleam@function:identity/1
),
_ = gleam_erlang_ffi:select(Selector@1),
logging:log(error, <<"Event loop has exited, bye bye"/utf8>>),
gleam_erlang_ffi:sleep(1000).