fork of https://gitlab.com/grom-gleam/grom altered to work with https://fluxer.app
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

new_with_init, all shards ready event

+135 -50
+18 -21
examples/your_first_bot/src/your_first_bot.gleam
··· 1 1 import dotenv_gleam 2 2 import envoy 3 - import gleam/erlang/process 3 + import gleam/erlang/process.{type Subject} 4 4 import gleam/option.{None, Some} 5 5 import gleam/string 6 6 import grom ··· 12 12 import logging 13 13 14 14 type State { 15 - State(client: grom.Client) 15 + State(client: grom.Client, gateway: Subject(gateway.Message)) 16 16 } 17 17 18 18 pub fn main() -> Nil { ··· 27 27 client 28 28 |> gateway.identify(intents: intent.all) 29 29 30 - let state = State(client:) 31 - 32 30 let assert Ok(data) = gateway.get_data(client) 33 31 34 32 let gateway_start_result = 35 - gateway.new(state, identify, data) 33 + gateway.new_with_initializer( 34 + fn(subject) { 35 + let state = State(client, subject) 36 + Ok(state) 37 + }, 38 + identify, 39 + data, 40 + ) 36 41 |> gateway.on_event(do: on_event) 37 42 |> gateway.start 38 43 ··· 95 100 } 96 101 } 97 102 98 - connection 103 + state.gateway 99 104 |> gateway.update_presence(using: gateway.UpdatePresenceMessage( 100 105 status: gateway.Online, 101 106 since: None, ··· 108 113 gateway.continue(state) 109 114 } 110 115 111 - fn on_interaction_created( 112 - state: State, 113 - interaction: Interaction, 114 - connection: gateway.Connection(State), 115 - ) { 116 + fn on_interaction_created(state: State, interaction: Interaction) { 116 117 case interaction.data { 117 118 interaction.CommandExecuted(command) -> 118 - on_command_executed(state, interaction, command, connection) 119 + on_command_executed(state, interaction, command) 119 120 _ -> gateway.continue(state) 120 121 } 121 122 } ··· 124 125 state: State, 125 126 interaction: Interaction, 126 127 command: interaction.CommandExecution, 127 - connection: gateway.Connection(State), 128 128 ) { 129 129 case command { 130 130 interaction.SlashCommandExecuted(command) -> 131 - on_slash_command_executed(state, interaction, command, connection) 131 + on_slash_command_executed(state, interaction, command) 132 132 _ -> gateway.continue(state) 133 133 } 134 134 } ··· 137 137 state: State, 138 138 interaction: Interaction, 139 139 command: interaction.SlashCommandExecution, 140 - connection: gateway.Connection(State), 141 140 ) { 142 141 case command.name { 143 142 "ping" -> on_ping_command(state, interaction) 144 - "soundboards" -> on_soundboards_command(state, interaction, connection) 145 - "join" -> on_join_command(state, interaction, connection) 143 + "soundboards" -> on_soundboards_command(state, interaction) 144 + "join" -> on_join_command(state, interaction) 146 145 _ -> gateway.continue(state) 147 146 } 148 147 } ··· 150 149 fn on_join_command( 151 150 state: State, 152 151 interaction: Interaction, 153 - connection: gateway.Connection(State), 154 152 ) -> gateway.Next(State) { 155 - connection 153 + state.gateway 156 154 |> gateway.update_voice_state(using: gateway.UpdateVoiceStateMessage( 157 155 "1155216444691325049", 158 156 Some("1155216445211422795"), ··· 178 176 fn on_soundboards_command( 179 177 state: State, 180 178 interaction: Interaction, 181 - connection: gateway.Connection(State), 182 179 ) -> gateway.Next(State) { 183 - connection 180 + state.gateway 184 181 |> gateway.request_soundboard_sounds(for: ["1155216444691325049"]) 185 182 186 183 let _response_result =
+117 -29
src/grom/gateway.gleam
··· 37 37 import grom/internal/time_rfc3339 38 38 import grom/internal/time_timestamp 39 39 import grom/invite 40 - import grom/message.{type Message} 40 + import grom/message 41 41 import grom/message/reaction 42 42 import grom/modification.{type Modification, Skip} 43 43 import grom/soundboard ··· 69 69 70 70 pub type Event { 71 71 ReadyEvent(ReadyMessage) 72 + AllShardsReadyEvent(AllShardsReadyMessage) 72 73 ErrorEvent(grom.Error) 73 74 ResumedEvent 74 75 RateLimitedEvent(RateLimitedMessage) ··· 282 283 ) 283 284 } 284 285 286 + pub type AllShardsReadyMessage { 287 + AllShardsReadyMessage( 288 + api_version: Int, 289 + user: User, 290 + guilds: List(guild.UnavailableGuild), 291 + application: ReadyApplication, 292 + shard_count: Int, 293 + ) 294 + } 295 + 285 296 pub type RateLimitedMessage { 286 297 RateLimitedMessage( 287 298 limited_opcode: Int, ··· 529 540 530 541 pub type MessageCreatedMessage { 531 542 MessageCreatedMessage( 532 - message: Message, 543 + message: message.Message, 533 544 guild_id: Option(String), 534 545 member: Option(GuildMember), 535 546 mentions: List(User), ··· 538 549 539 550 pub type MessageUpdatedMessage { 540 551 MessageUpdatedMessage( 541 - message: Message, 552 + message: message.Message, 542 553 guild_id: Option(String), 543 554 member: Option(GuildMember), 544 555 mentions: List(User), ··· 754 765 Builder( 755 766 identify: BaseIdentifyMessage, 756 767 data: Data, 757 - init: fn() -> state, 768 + init: fn(Subject(Message)) -> Result(state, String), 758 769 handler: fn(state, Event) -> Next(state), 759 770 close: fn(state) -> Nil, 760 771 shard_count: Option(Int), ··· 770 781 GettingReady( 771 782 gateway_url: String, 772 783 manager: Subject(ConnectionManagerMessage), 773 - subject: Subject(GatewayMessage), 784 + subject: Subject(Message), 774 785 identify: IdentifyMessage, 775 786 ) 776 787 Welcomed( 777 788 gateway_url: String, 778 789 manager: Subject(ConnectionManagerMessage), 779 - subject: Subject(GatewayMessage), 790 + subject: Subject(Message), 780 791 identify: IdentifyMessage, 781 792 heartbeat_manager: Subject(HeartbeatManagerMessage), 782 793 sequence: Option(Int), ··· 784 795 Identified( 785 796 gateway_url: String, 786 797 manager: Subject(ConnectionManagerMessage), 787 - subject: Subject(GatewayMessage), 798 + subject: Subject(Message), 788 799 identify: IdentifyMessage, 789 800 heartbeat_manager: Subject(HeartbeatManagerMessage), 790 801 sequence: Option(Int), ··· 824 835 shard_count: Int, 825 836 event_handler: fn(user_state, Event) -> Next(user_state), 826 837 shards: List(#(Shard, Subject(ConnectionManagerMessage))), 838 + all_ready: Option(AllShardsReadyMessage), 827 839 ) 828 840 } 829 841 ··· 835 847 ShardStopAbnormal(reason: String) 836 848 } 837 849 838 - pub opaque type GatewayMessage { 850 + pub opaque type Message { 839 851 MessageFromUser(UserMessage) 840 852 MessageFromDiscord(event: Event, reply_to: Subject(NextForShards)) 841 853 RegisterShard(#(Shard, Subject(ConnectionManagerMessage))) ··· 2304 2316 StopAbnormal(reason:) 2305 2317 } 2306 2318 2319 + /// Only use this function if you have no intention of sending user messages, such as: 2320 + /// * `UpdatePresence` 2321 + /// * `UpdateVoiceState` 2322 + /// * `RequestGuildMembers` 2323 + /// * `RequestSoundboardSounds` 2307 2324 pub fn new( 2308 2325 state: state, 2309 2326 identify: BaseIdentifyMessage, ··· 2312 2329 Builder( 2313 2330 identify:, 2314 2331 data:, 2315 - init: fn() { state }, 2332 + init: fn(_) { Ok(state) }, 2333 + handler: fn(state, _event) { continue(state) }, 2334 + close: fn(_state) { Nil }, 2335 + shard_count: None, 2336 + ) 2337 + } 2338 + 2339 + /// You should hold the gateway subject in your state. 2340 + /// You'll send user messages to that subject. 2341 + pub fn new_with_initializer( 2342 + init: fn(Subject(Message)) -> Result(user_state, String), 2343 + identify: BaseIdentifyMessage, 2344 + data: Data, 2345 + ) -> Builder(user_state) { 2346 + Builder( 2347 + identify:, 2348 + data:, 2349 + init:, 2316 2350 handler: fn(state, _event) { continue(state) }, 2317 2351 close: fn(_state) { Nil }, 2318 2352 shard_count: None, ··· 2326 2360 Builder(..builder, handler:) 2327 2361 } 2328 2362 2363 + pub fn on_close( 2364 + builder: Builder(state), 2365 + do handler: fn(state) -> Nil, 2366 + ) -> Builder(state) { 2367 + Builder(..builder, close: handler) 2368 + } 2369 + 2329 2370 /// Use this to force the amount of shards to a set number. 2330 2371 /// By default, grom will resort to the recommended shard count given to us from Discord. 2331 2372 /// Unless you have more than 150,000 guilds using your bot, you likely shouldn't use this function. ··· 2338 2379 2339 2380 pub fn start( 2340 2381 builder: Builder(state), 2341 - ) -> Result(actor.Started(Subject(GatewayMessage)), actor.StartError) { 2382 + ) -> Result(actor.Started(Subject(Message)), actor.StartError) { 2342 2383 let shard_count = case builder.shard_count { 2343 2384 Some(count) -> count 2344 2385 None -> builder.data.recommended_shards ··· 2347 2388 builder.data.session_start_limits.max_identify_requests_per_5_seconds 2348 2389 let supervisor = static_supervisor.new(static_supervisor.OneForOne) 2349 2390 2350 - let state = 2351 - Gateway( 2352 - user_state: builder.init(), 2353 - identify: builder.identify, 2354 - data: builder.data, 2355 - event_handler: builder.handler, 2356 - shard_count:, 2357 - shards: [], 2358 - ) 2359 - 2360 2391 let shard_ids = list.range(from: 0, to: shard_count) 2361 2392 let shards = 2362 2393 shard_ids ··· 2373 2404 }) 2374 2405 2375 2406 actor.new_with_initialiser(1000, fn(subject) { 2407 + use user_state <- result.try(builder.init(subject)) 2408 + 2409 + let state = 2410 + Gateway( 2411 + user_state:, 2412 + identify: builder.identify, 2413 + data: builder.data, 2414 + event_handler: builder.handler, 2415 + shard_count:, 2416 + shards: [], 2417 + all_ready: None, 2418 + ) 2419 + 2376 2420 // i have no idea whether this is actually required 2377 2421 let selector = 2378 2422 process.new_selector() ··· 2436 2480 2437 2481 fn on_gateway_message( 2438 2482 gateway: Gateway(user_state), 2439 - message: GatewayMessage, 2483 + message: Message, 2440 2484 ) -> actor.Next(Gateway(user_state), a) { 2441 2485 case message { 2442 2486 MessageFromUser(msg) -> { ··· 2465 2509 2466 2510 actor.continue(gateway) 2467 2511 } 2512 + MessageFromDiscord(ReadyEvent(ready), reply_to) -> { 2513 + let all_ready = case gateway.all_ready { 2514 + Some(old) -> 2515 + AllShardsReadyMessage( 2516 + ..old, 2517 + guilds: list.append(old.guilds, ready.guilds), 2518 + shard_count: old.shard_count + 1, 2519 + ) 2520 + None -> 2521 + AllShardsReadyMessage( 2522 + api_version: ready.api_version, 2523 + user: ready.user, 2524 + guilds: ready.guilds, 2525 + application: ready.application, 2526 + shard_count: 1, 2527 + ) 2528 + } 2529 + 2530 + let next = case all_ready.shard_count == gateway.shard_count { 2531 + True -> 2532 + gateway.event_handler( 2533 + gateway.user_state, 2534 + AllShardsReadyEvent(all_ready), 2535 + ) 2536 + False -> gateway.event_handler(gateway.user_state, ReadyEvent(ready)) 2537 + } 2538 + 2539 + case next { 2540 + Continue(state) -> { 2541 + process.send(reply_to, ShardContinue) 2542 + actor.continue( 2543 + Gateway(..gateway, user_state: state, all_ready: Some(all_ready)), 2544 + ) 2545 + } 2546 + Stop -> { 2547 + process.send(reply_to, ShardStop) 2548 + actor.stop() 2549 + } 2550 + StopAbnormal(reason) -> { 2551 + process.send(reply_to, ShardStopAbnormal(reason)) 2552 + actor.stop_abnormal(reason) 2553 + } 2554 + } 2555 + } 2468 2556 MessageFromDiscord(msg, reply_to) -> { 2469 2557 let next = gateway.event_handler(gateway.user_state, msg) 2470 2558 case next { ··· 2492 2580 fn supervised_shard_spawner( 2493 2581 builder: Builder(user_state), 2494 2582 shard: BucketedShard, 2495 - subject: Subject(GatewayMessage), 2583 + subject: Subject(Message), 2496 2584 ) -> supervision.ChildSpecification(Subject(ShardSpawnerMessage)) { 2497 2585 supervision.supervisor(fn() { new_shard_spawner(builder, shard, subject) }) 2498 2586 } ··· 2500 2588 fn new_shard_spawner( 2501 2589 builder: Builder(user_state), 2502 2590 shard: BucketedShard, 2503 - subject: Subject(GatewayMessage), 2591 + subject: Subject(Message), 2504 2592 ) -> Result(actor.Started(Subject(ShardSpawnerMessage)), actor.StartError) { 2505 2593 actor.new_with_initialiser(50, fn(subject) { 2506 2594 let selector = ··· 2528 2616 fn on_shard_spawner_message( 2529 2617 builder: Builder(user_state), 2530 2618 shard: BucketedShard, 2531 - subject: Subject(GatewayMessage), 2619 + subject: Subject(Message), 2532 2620 ) -> fn(a, ShardSpawnerMessage) -> actor.Next(Nil, b) { 2533 2621 fn(_state, message: ShardSpawnerMessage) { 2534 2622 case message { ··· 2546 2634 fn start_connection( 2547 2635 builder: Builder(user_state), 2548 2636 shard: BucketedShard, 2549 - subject: Subject(GatewayMessage), 2637 + subject: Subject(Message), 2550 2638 ) -> Result(Nil, grom.Error) { 2551 2639 let request_url = 2552 2640 builder.data.url ··· 2810 2898 } 2811 2899 2812 2900 pub fn update_presence( 2813 - gateway: Subject(GatewayMessage), 2901 + gateway: Subject(Message), 2814 2902 using message: UpdatePresenceMessage, 2815 2903 ) -> Nil { 2816 2904 process.send(gateway, MessageFromUser(StartPresenceUpdate(message))) 2817 2905 } 2818 2906 2819 2907 pub fn update_voice_state( 2820 - gateway: Subject(GatewayMessage), 2908 + gateway: Subject(Message), 2821 2909 using message: UpdateVoiceStateMessage, 2822 2910 ) -> Nil { 2823 2911 process.send(gateway, MessageFromUser(StartVoiceStateUpdate(message))) 2824 2912 } 2825 2913 2826 2914 pub fn request_guild_members( 2827 - gateway: Subject(GatewayMessage), 2915 + gateway: Subject(Message), 2828 2916 using message: RequestGuildMembersMessage, 2829 2917 ) -> Nil { 2830 2918 process.send(gateway, MessageFromUser(StartGuildMembersRequest(message))) ··· 2836 2924 /// b) receive multiple SoundboardSounds events with different data based on the guild-shard relationship 2837 2925 /// Please report any usage of this function with multiple shards in issues. 2838 2926 pub fn request_soundboard_sounds( 2839 - gateway: Subject(GatewayMessage), 2927 + gateway: Subject(Message), 2840 2928 for guild_ids: List(String), 2841 2929 ) -> Nil { 2842 2930 process.send(