-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", 32). ?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.1)"/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, <<"Initializing builder"/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}, case discord_gleam@ws@packets@hello:string_to_data( Msg@1 ) of {ok, Data} -> gleam@erlang@process:start( fun() -> repeatedly_ffi:call( erlang:element( 2, erlang:element(3, Data) ), 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>>, case S@2 of 0 -> gleam@json:null( ); _ -> gleam@json:int( S@2 ) end}] ), gleam@json:to_string( _pipe@8 ) end, logging:log( debug, <<"Sending heartbeat: "/utf8, Packet/binary>> ), stratus:send_text_message( Conn, Packet ) end ) end, false ), 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), logging:log( critical, <<"Closing websocket due to fatal error"/utf8>> ) end, 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 => 264}) 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).