discorb 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build_version.yml +2 -2
  3. data/.rubocop.yml +12 -75
  4. data/Changelog.md +25 -0
  5. data/Gemfile +4 -4
  6. data/README.md +2 -1
  7. data/Rakefile +482 -459
  8. data/Steepfile +8 -6
  9. data/docs/application_command.md +1 -0
  10. data/docs/events.md +2 -2
  11. data/docs/voice_events.md +6 -6
  12. data/lib/discorb/allowed_mentions.rb +68 -72
  13. data/lib/discorb/app_command/command.rb +466 -394
  14. data/lib/discorb/app_command/common.rb +65 -25
  15. data/lib/discorb/app_command/handler.rb +304 -265
  16. data/lib/discorb/app_command.rb +5 -5
  17. data/lib/discorb/application.rb +198 -197
  18. data/lib/discorb/asset.rb +101 -101
  19. data/lib/discorb/attachment.rb +134 -119
  20. data/lib/discorb/audit_logs.rb +412 -385
  21. data/lib/discorb/automod.rb +279 -269
  22. data/lib/discorb/channel/base.rb +107 -108
  23. data/lib/discorb/channel/category.rb +32 -32
  24. data/lib/discorb/channel/container.rb +44 -44
  25. data/lib/discorb/channel/dm.rb +26 -28
  26. data/lib/discorb/channel/guild.rb +311 -246
  27. data/lib/discorb/channel/stage.rb +156 -140
  28. data/lib/discorb/channel/text.rb +430 -336
  29. data/lib/discorb/channel/thread.rb +374 -325
  30. data/lib/discorb/channel/voice.rb +85 -79
  31. data/lib/discorb/channel.rb +5 -5
  32. data/lib/discorb/client.rb +635 -623
  33. data/lib/discorb/color.rb +178 -182
  34. data/lib/discorb/common.rb +168 -164
  35. data/lib/discorb/components/button.rb +107 -106
  36. data/lib/discorb/components/select_menu.rb +157 -145
  37. data/lib/discorb/components/text_input.rb +103 -106
  38. data/lib/discorb/components.rb +68 -66
  39. data/lib/discorb/dictionary.rb +135 -135
  40. data/lib/discorb/embed.rb +404 -398
  41. data/lib/discorb/emoji.rb +309 -302
  42. data/lib/discorb/emoji_table.rb +16099 -8857
  43. data/lib/discorb/error.rb +131 -131
  44. data/lib/discorb/event.rb +360 -314
  45. data/lib/discorb/event_handler.rb +39 -39
  46. data/lib/discorb/exe/about.rb +17 -17
  47. data/lib/discorb/exe/irb.rb +72 -67
  48. data/lib/discorb/exe/new.rb +323 -315
  49. data/lib/discorb/exe/run.rb +69 -68
  50. data/lib/discorb/exe/setup.rb +57 -55
  51. data/lib/discorb/exe/show.rb +12 -12
  52. data/lib/discorb/extend.rb +25 -45
  53. data/lib/discorb/extension.rb +89 -83
  54. data/lib/discorb/flag.rb +126 -128
  55. data/lib/discorb/gateway.rb +984 -794
  56. data/lib/discorb/gateway_events.rb +670 -638
  57. data/lib/discorb/gateway_requests.rb +45 -48
  58. data/lib/discorb/guild.rb +2115 -1626
  59. data/lib/discorb/guild_template.rb +280 -241
  60. data/lib/discorb/http.rb +247 -232
  61. data/lib/discorb/image.rb +42 -42
  62. data/lib/discorb/integration.rb +169 -161
  63. data/lib/discorb/intents.rb +161 -163
  64. data/lib/discorb/interaction/autocomplete.rb +76 -62
  65. data/lib/discorb/interaction/command.rb +279 -224
  66. data/lib/discorb/interaction/components.rb +114 -104
  67. data/lib/discorb/interaction/modal.rb +36 -32
  68. data/lib/discorb/interaction/response.rb +379 -330
  69. data/lib/discorb/interaction/root.rb +271 -118
  70. data/lib/discorb/interaction.rb +5 -5
  71. data/lib/discorb/invite.rb +154 -153
  72. data/lib/discorb/member.rb +344 -311
  73. data/lib/discorb/message.rb +615 -544
  74. data/lib/discorb/message_meta.rb +197 -186
  75. data/lib/discorb/modules.rb +371 -290
  76. data/lib/discorb/permission.rb +305 -289
  77. data/lib/discorb/presence.rb +352 -346
  78. data/lib/discorb/rate_limit.rb +81 -76
  79. data/lib/discorb/reaction.rb +55 -54
  80. data/lib/discorb/role.rb +272 -240
  81. data/lib/discorb/shard.rb +76 -74
  82. data/lib/discorb/sticker.rb +193 -171
  83. data/lib/discorb/user.rb +205 -188
  84. data/lib/discorb/utils/colored_puts.rb +16 -16
  85. data/lib/discorb/utils.rb +12 -16
  86. data/lib/discorb/voice_state.rb +305 -281
  87. data/lib/discorb/webhook.rb +537 -507
  88. data/lib/discorb.rb +62 -56
  89. data/sig/discorb/activity.rbs +1 -0
  90. data/sig/discorb/allowed_mentions.rbs +1 -0
  91. data/sig/discorb/app_command/base.rbs +7 -1
  92. data/sig/discorb/application.rbs +6 -0
  93. data/sig/discorb/asset.rbs +2 -0
  94. data/sig/discorb/attachment.rbs +8 -0
  95. data/sig/discorb/audit_log.rbs +7 -0
  96. data/sig/discorb/automod.rbs +32 -6
  97. data/sig/discorb/avatar.rbs +1 -0
  98. data/sig/discorb/channel/base.rbs +8 -1
  99. data/sig/discorb/channel/category.rbs +1 -0
  100. data/sig/discorb/channel/container.rbs +4 -0
  101. data/sig/discorb/channel/stage.rbs +4 -0
  102. data/sig/discorb/channel/text.rbs +2 -2
  103. data/sig/discorb/channel/thread.rbs +11 -0
  104. data/sig/discorb/channel/voice.rbs +2 -0
  105. data/sig/discorb/client.rbs +21 -20
  106. data/sig/discorb/color.rbs +6 -0
  107. data/sig/discorb/component/base.rbs +1 -0
  108. data/sig/discorb/component/button.rbs +2 -0
  109. data/sig/discorb/component/select_menu.rbs +4 -0
  110. data/sig/discorb/component/text_input.rbs +1 -0
  111. data/sig/discorb/custom_emoji.rbs +5 -1
  112. data/sig/discorb/dictionary.rbs +2 -0
  113. data/sig/discorb/discord_model.rbs +2 -0
  114. data/sig/discorb/embed.rbs +7 -0
  115. data/sig/discorb/emoji.rbs +1 -0
  116. data/sig/discorb/event_handler.rbs +2 -1
  117. data/sig/discorb/extension.rbs +13 -12
  118. data/sig/discorb/flag.rbs +2 -0
  119. data/sig/discorb/gateway.rbs +5 -0
  120. data/sig/discorb/guild.rbs +8 -4
  121. data/sig/discorb/guild_template.rbs +1 -1
  122. data/sig/discorb/http.rbs +4 -1
  123. data/sig/discorb/image.rbs +2 -0
  124. data/sig/discorb/integration.rbs +1 -1
  125. data/sig/discorb/intents.rbs +4 -3
  126. data/sig/discorb/interaction/base.rbs +36 -0
  127. data/sig/discorb/interaction/message_component.rbs +1 -2
  128. data/sig/discorb/interaction/modal.rbs +1 -2
  129. data/sig/discorb/interaction/responder.rbs +49 -49
  130. data/sig/discorb/invite.rbs +1 -1
  131. data/sig/discorb/member.rbs +2 -0
  132. data/sig/discorb/message.rbs +8 -1
  133. data/sig/discorb/messageable.rbs +1 -4
  134. data/sig/discorb/partial_emoji.rbs +3 -0
  135. data/sig/discorb/permissions.rbs +7 -0
  136. data/sig/discorb/presence.rbs +2 -0
  137. data/sig/discorb/reaction.rbs +5 -1
  138. data/sig/discorb/role.rbs +7 -1
  139. data/sig/discorb/scheduled_event.rbs +2 -1
  140. data/sig/discorb/shard.rbs +2 -1
  141. data/sig/discorb/snowflake.rbs +2 -0
  142. data/sig/discorb/stage_instance.rbs +9 -3
  143. data/sig/discorb/sticker.rbs +1 -1
  144. data/sig/discorb/unicode_emoji.rbs +4 -0
  145. data/sig/discorb/user.rbs +24 -20
  146. data/sig/discorb/webhook.rbs +17 -6
  147. data/sig/discorb/welcome_screen.rbs +1 -0
  148. data/sig/override.rbs +2 -0
  149. metadata +3 -3
@@ -1,794 +1,984 @@
1
- # frozen_string_literal: true
2
-
3
- require "async/http"
4
- require "async/websocket"
5
- require "async/barrier"
6
- require "json"
7
- require "zlib"
8
-
9
- module Discorb
10
- #
11
- # A module for Discord Gateway.
12
- # This module is internal use only.
13
- #
14
- module Gateway
15
- #
16
- # A module to handle gateway events.
17
- #
18
- module Handler
19
- # @type instance: Discorb::Client
20
-
21
- private
22
-
23
- def connect_gateway(reconnect)
24
- Async do
25
- @mutex["gateway_#{shard_id}"] ||= Mutex.new
26
- @mutex["gateway_#{shard_id}"].synchronize do
27
- if reconnect
28
- logger.info "Reconnecting to gateway..."
29
- else
30
- logger.info "Connecting to gateway..."
31
- end
32
-
33
- @http = HTTP.new(self)
34
- _, gateway_response = @http.request(Route.new("/gateway", "//gateway", :get)).wait
35
- gateway_url = gateway_response[:url]
36
- gateway_version = if @intents.to_h[:message_content].nil?
37
- unless @message_content_intent_warned
38
- warn "message_content intent not set, using gateway version 9. " \
39
- "You should specify `message_content` intent for preventing unexpected changes in the future."
40
- @message_content_intent_warned = true
41
- end
42
- 9
43
- else
44
- 10
45
- end
46
- endpoint = Async::HTTP::Endpoint.parse(
47
- "#{gateway_url}?v=#{gateway_version}&encoding=json&compress=zlib-stream&_=#{Time.now.to_i}",
48
- alpn_protocols: Async::HTTP::Protocol::HTTP11.names,
49
- )
50
- begin
51
- self.connection = Async::WebSocket::Client.connect(
52
- endpoint,
53
- headers: [["User-Agent", Discorb::USER_AGENT]],
54
- handler: RawConnection,
55
- )
56
- con = self.connection
57
- zlib_stream = Zlib::Inflate.new(Zlib::MAX_WBITS)
58
- buffer = +""
59
- begin
60
- while (message = con.read)
61
- buffer << message
62
- if message.end_with?((+"\x00\x00\xff\xff").force_encoding("ASCII-8BIT"))
63
- begin
64
- data = zlib_stream.inflate(buffer)
65
- buffer = +""
66
- message = JSON.parse(data, symbolize_names: true)
67
- rescue JSON::ParserError
68
- buffer = +""
69
- logger.error "Received invalid JSON from gateway."
70
- logger.debug "#{data}"
71
- else
72
- handle_gateway(message, reconnect)
73
- end
74
- end
75
- end
76
- rescue Async::Wrapper::Cancelled,
77
- OpenSSL::SSL::SSLError,
78
- Async::Wrapper::WaitError,
79
- EOFError,
80
- Errno::EPIPE,
81
- Errno::ECONNRESET,
82
- IOError => e
83
- next if @status == :closed
84
-
85
- logger.error "Gateway connection closed accidentally: #{e.class}: #{e.message}"
86
- con.force_close
87
- connect_gateway(true)
88
- next
89
- end
90
- rescue Protocol::WebSocket::ClosedError => e
91
- @tasks.map(&:stop)
92
- case e.code
93
- when 4004
94
- raise ClientError.new("Authentication failed"), cause: nil
95
- when 4009
96
- logger.info "Session timed out, reconnecting."
97
- con.force_close
98
- connect_gateway(true)
99
- next
100
- when 4014
101
- raise ClientError.new("Disallowed intents were specified"), cause: nil
102
- when 4001, 4002, 4003, 4005, 4007
103
- raise ClientError.new(<<~ERROR), cause: e
104
- Disconnected from gateway, probably due to library issues.
105
- #{e.message}
106
-
107
- Please report this to the library issue tracker.
108
- https://github.com/discorb-lib/discorb/issues
109
- ERROR
110
- when 1001
111
- logger.info "Gateway closed with code 1001, reconnecting."
112
- con.force_close
113
- connect_gateway(true)
114
- next
115
- else
116
- logger.error "Discord WebSocket closed with code #{e.code}."
117
- logger.debug "#{e.message}"
118
- con.force_close
119
- connect_gateway(false)
120
- next
121
- end
122
- rescue StandardError => e
123
- logger.error "Discord WebSocket error: #{e.full_message}"
124
- con.force_close
125
- connect_gateway(false)
126
- next
127
- end
128
- end
129
- end
130
- end
131
-
132
- def send_gateway(opcode, **value)
133
- if @shards.any? && shard.nil?
134
- @shards.map(&:connection)
135
- else
136
- [connection]
137
- end.each do |con|
138
- con.write({ op: opcode, d: value }.to_json)
139
- con.flush
140
- end
141
- logger.debug "Sent message to fd #{connection.io.fileno}: #{{ op: opcode, d: value }.to_json.gsub(@token,
142
- "[Token]")}"
143
- end
144
-
145
- def handle_gateway(payload, reconnect)
146
- Async do |_task|
147
- data = payload[:d]
148
- @last_s = payload[:s] if payload[:s]
149
- logger.debug "Received message with opcode #{payload[:op]} from gateway."
150
- logger.debug "#{payload.to_json.gsub(@token, "[Token]")}"
151
- case payload[:op]
152
- when 10
153
- @heartbeat_interval = data[:heartbeat_interval]
154
- if reconnect
155
- payload = {
156
- token: @token,
157
- session_id: session_id,
158
- seq: @last_s,
159
- }
160
- send_gateway(6, **payload)
161
- else
162
- payload = {
163
- token: @token,
164
- intents: @intents.value,
165
- compress: false,
166
- properties: { "$os" => RUBY_PLATFORM, "$browser" => "discorb", "$device" => "discorb" },
167
- }
168
- payload[:shard] = [shard_id, @shard_count] if shard_id
169
- payload[:presence] = @identify_presence if @identify_presence
170
- send_gateway(2, **payload)
171
- end
172
- when 7
173
- logger.info "Received opcode 7, stopping tasks"
174
- @tasks.map(&:stop)
175
- when 9
176
- logger.warn "Received opcode 9, closed connection"
177
- @tasks.map(&:stop)
178
- if data
179
- logger.info "Connection is resumable, reconnecting"
180
- connection.force_close
181
- connect_gateway(true)
182
- else
183
- logger.info "Connection is not resumable, reconnecting with opcode 2"
184
- connection.force_close
185
-
186
- sleep(2)
187
- connect_gateway(false)
188
- end
189
- when 11
190
- logger.debug "Received opcode 11"
191
- @ping = Time.now.to_f - @heartbeat_before
192
- when 0
193
- handle_event(payload[:t], data)
194
- end
195
- end
196
- end
197
-
198
- def handle_heartbeat
199
- Async do |_task|
200
- interval = @heartbeat_interval
201
- sleep((interval / 1000.0 - 1) * rand)
202
- loop do
203
- unless connection.closed?
204
- @heartbeat_before = Time.now.to_f
205
- connection.write({ op: 1, d: @last_s }.to_json)
206
- connection.flush
207
- logger.debug "Sent opcode 1."
208
- logger.debug "Waiting for heartbeat."
209
- end
210
- sleep(interval / 1000.0 - 1)
211
- end
212
- end
213
- end
214
-
215
- def handle_event(event_name, data)
216
- return logger.debug "Client isn't ready; event #{event_name} wasn't handled" if @wait_until_ready &&
217
- !@ready &&
218
- !%w[
219
- READY GUILD_CREATE
220
- ].include?(event_name)
221
-
222
- dispatch(:event_receive, event_name, data)
223
- logger.debug "Handling event #{event_name}"
224
- case event_name
225
- when "READY"
226
- @api_version = data[:v]
227
- self.session_id = data[:session_id]
228
- @user = ClientUser.new(self, data[:user])
229
- @uncached_guilds = data[:guilds].map { |g| g[:id] }
230
- ready if (@uncached_guilds == []) || !@intents.guilds
231
- dispatch(:ready)
232
-
233
- @tasks << handle_heartbeat
234
- when "GUILD_CREATE"
235
- if @uncached_guilds.include?(data[:id])
236
- Guild.new(self, data, true)
237
- @uncached_guilds.delete(data[:id])
238
- if @uncached_guilds == []
239
- logger.debug "All guilds cached"
240
- ready
241
- end
242
- elsif @guilds.has?(data[:id])
243
- @guilds[data[:id]].send(:_set_data, data, true)
244
- dispatch(:guild_available, guild)
245
- else
246
- guild = Guild.new(self, data, true)
247
- dispatch(:guild_join, guild)
248
- end
249
- dispatch(:guild_create, @guilds[data[:id]])
250
- when "MESSAGE_CREATE"
251
- message = Message.new(self, data)
252
- dispatch(:message, message)
253
- when "GUILD_UPDATE"
254
- if @guilds.has?(data[:id])
255
- current = @guilds[data[:id]]
256
- before = Guild.new(self, current.instance_variable_get(:@data).merge(no_cache: true), false)
257
- current.send(:_set_data, data, false)
258
- dispatch(:guild_update, before, current)
259
- else
260
- logger.warn "Unknown guild id #{data[:id]}, ignoring"
261
- end
262
- when "GUILD_DELETE"
263
- return logger.warn "Unknown guild id #{data[:id]}, ignoring" unless (guild = @guilds.delete(data[:id]))
264
-
265
- dispatch(:guild_delete, guild)
266
- if data[:unavailable]
267
- dispatch(:guild_destroy, guild)
268
- else
269
- dispatch(:guild_leave, guild)
270
- end
271
- when "GUILD_ROLE_CREATE"
272
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
273
-
274
- nr = Role.new(@client, guild, data[:role])
275
- guild.roles[data[:role][:id]] = nr
276
- dispatch(:role_create, nr)
277
- when "GUILD_ROLE_UPDATE"
278
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
279
- return logger.warn "Unknown role id #{data[:role][:id]}, ignoring" unless guild.roles.has?(data[:role][:id])
280
-
281
- current = guild.roles[data[:role][:id]]
282
- before = Role.new(@client, guild, current.instance_variable_get(:@data).update({ no_cache: true }))
283
- current.send(:_set_data, data[:role])
284
- dispatch(:role_update, before, current)
285
- when "GUILD_ROLE_DELETE"
286
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
287
- unless (role = guild.roles.delete(data[:role_id]))
288
- return logger.warn "Unknown role id #{data[:role_id]}, ignoring"
289
- end
290
-
291
- dispatch(:role_delete, role)
292
- when "CHANNEL_CREATE"
293
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
294
-
295
- nc = Channel.make_channel(self, data)
296
- guild.channels[data[:id]] = nc
297
-
298
- dispatch(:channel_create, nc)
299
- when "CHANNEL_UPDATE"
300
- return logger.warn "Unknown channel id #{data[:id]}, ignoring" unless (current = @channels[data[:id]])
301
-
302
- before = Channel.make_channel(self, current.instance_variable_get(:@data), no_cache: true)
303
- current.send(:_set_data, data)
304
- dispatch(:channel_update, before, current)
305
- when "CHANNEL_DELETE"
306
- return logger.warn "Unknown channel id #{data[:id]}, ignoring" unless (channel = @channels.delete(data[:id]))
307
-
308
- @guilds[data[:guild_id]]&.channels&.delete(data[:id])
309
- dispatch(:channel_delete, channel)
310
- when "CHANNEL_PINS_UPDATE"
311
- nil # do in MESSAGE_UPDATE
312
- when "THREAD_CREATE"
313
- thread = Channel.make_channel(self, data)
314
-
315
- dispatch(:thread_create, thread)
316
- if data.key?(:member)
317
- dispatch(:thread_join, thread)
318
- else
319
- dispatch(:thread_new, thread)
320
- end
321
- when "THREAD_UPDATE"
322
- return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
323
-
324
- before = Channel.make_channel(self, thread.instance_variable_get(:@data), no_cache: true)
325
- thread.send(:_set_data, data)
326
- dispatch(:thread_update, before, thread)
327
- when "THREAD_DELETE"
328
- return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels.delete(data[:id]))
329
-
330
- @guilds[data[:guild_id]]&.channels&.delete(data[:id])
331
- dispatch(:thread_delete, thread)
332
- when "THREAD_LIST_SYNC"
333
- data[:threads].each do |raw_thread|
334
- thread = Channel.make_channel(self, raw_thread.merge({ member: raw_thread[:members].find do |m|
335
- m[:id] == raw_thread[:id]
336
- end }))
337
- @channels[thread.id] = thread
338
- end
339
- when "THREAD_MEMBER_UPDATE"
340
- return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
341
-
342
- if (member = thread.members[data[:id]])
343
- old = ThreadChannel::Member.new(self, member.instance_variable_get(:@data), data[:guild_id])
344
- member.send(:_set_data, data)
345
- else
346
- old = nil
347
- member = ThreadChannel::Member.new(self, data, data[:guild_id])
348
- thread.members[data[:user_id]] = member
349
- end
350
- dispatch(:thread_member_update, thread, old, member)
351
- when "THREAD_MEMBERS_UPDATE"
352
- return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
353
-
354
- thread.instance_variable_set(:@member_count, data[:member_count])
355
- members = []
356
- (data[:added_members] || []).each do |raw_member|
357
- member = ThreadChannel::Member.new(self, raw_member, data[:guild_id])
358
- thread.members[member.id] = member
359
- members << member
360
- end
361
- removed_members = []
362
- (data[:removed_member_ids] || []).each do |id|
363
- removed_members << thread.members.delete(id)
364
- end
365
- dispatch(:thread_members_update, thread, members, removed_members)
366
- when "STAGE_INSTANCE_CREATE"
367
- instance = StageInstance.new(self, data)
368
- dispatch(:stage_instance_create, instance)
369
- when "STAGE_INSTANCE_UPDATE"
370
- unless (channel = @channels[data[:channel_id]])
371
- return logger.warn "Unknown channel id #{data[:channel_id]} , ignoring"
372
- end
373
- unless (instance = channel.stage_instances[data[:id]])
374
- return logger.warn "Unknown stage instance id #{data[:id]}, ignoring"
375
- end
376
-
377
- old = StageInstance.new(self, instance.instance_variable_get(:@data), no_cache: true)
378
- current.send(:_set_data, data)
379
- dispatch(:stage_instance_update, old, current)
380
- when "STAGE_INSTANCE_DELETE"
381
- unless (channel = @channels[data[:channel_id]])
382
- return logger.warn "Unknown channel id #{data[:channel_id]} , ignoring"
383
- end
384
- unless (instance = channel.stage_instances.delete(data[:id]))
385
- return logger.warn "Unknown stage instance id #{data[:id]}, ignoring"
386
- end
387
-
388
- dispatch(:stage_instance_delete, instance)
389
- when "GUILD_MEMBER_ADD"
390
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
391
-
392
- nm = Member.new(self, data[:guild_id], data[:user].update({ no_cache: true }), data)
393
- guild.members[nm.id] = nm
394
- dispatch(:member_add, nm)
395
- when "GUILD_MEMBER_UPDATE"
396
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
397
- unless (nm = guild.members[data[:user][:id]])
398
- return logger.warn "Unknown member id #{data[:user][:id]}, ignoring"
399
- end
400
-
401
- old = Member.new(self, data[:guild_id], data[:user], data.update({ no_cache: true }))
402
- nm.send(:_set_data, data[:user], data)
403
- dispatch(:member_update, old, nm)
404
- when "GUILD_MEMBER_REMOVE"
405
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
406
- unless (member = guild.members.delete(data[:user][:id]))
407
- return logger.warn "Unknown member id #{data[:user][:id]}, ignoring"
408
- end
409
-
410
- dispatch(:member_remove, member)
411
- when "GUILD_BAN_ADD"
412
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
413
-
414
- user = if @users.has? data[:user][:id]
415
- @users[data[:user][:id]]
416
- else
417
- User.new(self, data[:user].update({ no_cache: true }))
418
- end
419
-
420
- dispatch(:guild_ban_add, guild, user)
421
- when "GUILD_BAN_REMOVE"
422
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
423
-
424
- user = if @users.has? data[:user][:id]
425
- @users[data[:user][:id]]
426
- else
427
- User.new(self, data[:user].update({ no_cache: true }))
428
- end
429
-
430
- dispatch(:guild_ban_remove, guild, user)
431
- when "GUILD_EMOJIS_UPDATE"
432
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
433
-
434
- before_emojis = guild.emojis.values.map(&:id).to_set
435
- data[:emojis].each do |emoji|
436
- guild.emojis[emoji[:id]] = CustomEmoji.new(self, guild, emoji)
437
- end
438
- deleted_emojis = before_emojis - guild.emojis.values.map(&:id).to_set
439
- deleted_emojis.each do |emoji|
440
- guild.emojis.delete(emoji)
441
- end
442
- when "GUILD_INTEGRATIONS_UPDATE"
443
- dispatch(:guild_integrations_update, @guilds[data[:guild_id]])
444
- when "INTEGRATION_CREATE"
445
- dispatch(:integration_create, Integration.new(self, data, data[:guild_id]))
446
- when "INTEGRATION_UPDATE"
447
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
448
-
449
- integration = Integration.new(self, data, data[:guild_id])
450
- dispatch(:integration_update, integration)
451
- when "INTEGRATION_DELETE"
452
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
453
-
454
- dispatch(:integration_delete, IntegrationDeleteEvent.new(self, data))
455
- when "WEBHOOKS_UPDATE"
456
- dispatch(:webhooks_update, WebhooksUpdateEvent.new(self, data))
457
- when "INVITE_CREATE"
458
- dispatch(:invite_create, Invite.new(self, data, true))
459
- when "INVITE_DELETE"
460
- dispatch(:invite_delete, InviteDeleteEvent.new(self, data))
461
- when "VOICE_STATE_UPDATE"
462
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
463
-
464
- current = guild.voice_states[data[:user_id]]
465
- if current.nil?
466
- old = nil
467
- current = VoiceState.new(self, data)
468
- guild.voice_states[data[:user_id]] = current
469
- else
470
- guild.voice_states.remove(data[:user_id]) if data[:channel_id].nil?
471
- old = VoiceState.new(self, current.instance_variable_get(:@data))
472
- current.send(:_set_data, data)
473
- end
474
- dispatch(:voice_state_update, old, current)
475
- if old&.channel != current&.channel
476
- dispatch(:voice_channel_update, old, current)
477
- case [old&.channel.nil?, current&.channel.nil?]
478
- when [true, false]
479
- dispatch(:voice_channel_connect, current)
480
- when [false, true]
481
- dispatch(:voice_channel_disconnect, old)
482
- when [false, false]
483
- dispatch(:voice_channel_move, old, current)
484
- end
485
- end
486
- if old&.mute? != current&.mute?
487
- dispatch(:voice_mute_update, old, current)
488
- case [old&.mute?, current&.mute?]
489
- when [false, true]
490
- dispatch(:voice_mute_enable, current)
491
- when [true, false]
492
- dispatch(:voice_mute_disable, old)
493
- end
494
- end
495
- if old&.deaf? != current&.deaf?
496
- dispatch(:voice_deaf_update, old, current)
497
- case [old&.deaf?, current&.deaf?]
498
- when [false, true]
499
- dispatch(:voice_deaf_enable, current)
500
- when [true, false]
501
- dispatch(:voice_deaf_disable, old)
502
- end
503
- end
504
- if old&.self_mute? != current&.self_mute?
505
- dispatch(:voice_self_mute_update, old, current)
506
- case [old&.self_mute?, current&.self_mute?]
507
- when [false, true]
508
- dispatch(:voice_self_mute_enable, current)
509
- when [true, false]
510
- dispatch(:voice_self_mute_disable, old)
511
- end
512
- end
513
- if old&.self_deaf? != current&.self_deaf?
514
- dispatch(:voice_self_deaf_update, old, current)
515
- case [old&.self_deaf?, current&.self_deaf?]
516
- when [false, true]
517
- dispatch(:voice_self_deaf_enable, current)
518
- when [true, false]
519
- dispatch(:voice_self_deaf_disable, old)
520
- end
521
- end
522
- if old&.server_mute? != current&.server_mute?
523
- dispatch(:voice_server_mute_update, old, current)
524
- case [old&.server_mute?, current&.server_mute?]
525
- when [false, true]
526
- dispatch(:voice_server_mute_enable, current)
527
- when [true, false]
528
- dispatch(:voice_server_mute_disable, old)
529
- end
530
- end
531
- if old&.server_deaf? != current&.server_deaf?
532
- dispatch(:voice_server_deaf_update, old, current)
533
- case [old&.server_deaf?, current&.server_deaf?]
534
- when [false, true]
535
- dispatch(:voice_server_deaf_enable, current)
536
- when [true, false]
537
- dispatch(:voice_server_deaf_disable, old)
538
- end
539
- end
540
- if old&.video? != current&.video?
541
- dispatch(:voice_video_update, old, current)
542
- case [old&.video?, current&.video?]
543
- when [false, true]
544
- dispatch(:voice_video_start, current)
545
- when [true, false]
546
- dispatch(:voice_video_end, old)
547
- end
548
- end
549
- if old&.stream? != current&.stream?
550
- dispatch(:voice_stream_update, old, current)
551
- case [old&.stream?, current&.stream?]
552
- when [false, true]
553
- dispatch(:voice_stream_start, current)
554
- when [true, false]
555
- dispatch(:voice_stream_end, old)
556
- end
557
- end
558
- when "PRESENCE_UPDATE"
559
- return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
560
-
561
- guild.presences[data[:user][:id]] = Presence.new(self, data)
562
- when "MESSAGE_UPDATE"
563
- if (message = @messages[data[:id]])
564
- before = Message.new(self, message.instance_variable_get(:@data), no_cache: true)
565
- message.send(:_set_data, message.instance_variable_get(:@data).merge(data))
566
- else
567
- before = nil
568
- message = nil
569
- end
570
- if data[:edited_timestamp].nil?
571
- if message.nil?
572
- nil
573
- elsif message.pinned?
574
- message.instance_variable_set(:@pinned, false)
575
- else
576
- message.instance_variable_set(:@pinned, true)
577
- end
578
- dispatch(:message_pin_update, MessagePinEvent.new(self, data, message))
579
- else
580
- dispatch(:message_update, MessageUpdateEvent.new(self, data, before, current))
581
- end
582
- when "MESSAGE_DELETE"
583
- message.instance_variable_set(:@deleted, true) if (message = @messages[data[:id]])
584
-
585
- dispatch(:message_delete_id, Snowflake.new(data[:id]), channels[data[:channel_id]],
586
- data[:guild_id] && guilds[data[:guild_id]])
587
- dispatch(:message_delete, message, channels[data[:channel_id]], data[:guild_id] && guilds[data[:guild_id]])
588
- when "MESSAGE_DELETE_BULK"
589
- messages = []
590
- data[:ids].each do |id|
591
- if (message = @messages[id])
592
- message.instance_variable_set(:@deleted, true)
593
- messages.push(message)
594
- else
595
- messages.push(UnknownDeleteBulkMessage.new(self, id, data))
596
- end
597
- end
598
- dispatch(:message_delete_bulk, messages)
599
- when "MESSAGE_REACTION_ADD"
600
- if (target_message = @messages[data[:message_id]])
601
- if (target_reaction = target_message.reactions.find do |r|
602
- r.emoji.is_a?(UnicodeEmoji) ? r.emoji.value == data[:emoji][:name] : r.emoji.id == data[:emoji][:id]
603
- end)
604
- target_reaction.instance_variable_set(:@count, target_reaction.count + 1)
605
- else
606
- target_message.reactions << Reaction.new(
607
- target_message,
608
- {
609
- count: 1,
610
- me: @user.id == data[:user_id],
611
- emoji: data[:emoji],
612
- }
613
- )
614
- end
615
- end
616
- dispatch(:reaction_add, ReactionEvent.new(self, data))
617
- when "MESSAGE_REACTION_REMOVE"
618
- if (target_message = @messages[data[:message_id]]) &&
619
- (target_reaction = target_message.reactions.find do |r|
620
- data[:emoji][:id].nil? ? r.emoji.name == data[:emoji][:name] : r.emoji.id == data[:emoji][:id]
621
- end)
622
- target_reaction.instance_variable_set(:@count, target_reaction.count - 1)
623
- target_message.reactions.delete(target_reaction) if target_reaction.count.zero?
624
- end
625
- dispatch(:reaction_remove, ReactionEvent.new(self, data))
626
- when "MESSAGE_REACTION_REMOVE_ALL"
627
- if (target_message = @messages[data[:message_id]])
628
- target_message.reactions = []
629
- end
630
- dispatch(:reaction_remove_all, ReactionRemoveAllEvent.new(self, data))
631
- when "MESSAGE_REACTION_REMOVE_EMOJI"
632
- if (target_message = @messages[data[:message_id]]) &&
633
- (target_reaction = target_message.reactions.find do |r|
634
- data[:emoji][:id].nil? ? r.name == data[:emoji][:name] : r.id == data[:emoji][:id]
635
- end)
636
- target_message.reactions.delete(target_reaction)
637
- end
638
- dispatch(:reaction_remove_emoji, ReactionRemoveEmojiEvent.new(self, data))
639
- when "TYPING_START"
640
- dispatch(:typing_start, TypingStartEvent.new(self, data))
641
- when "INTERACTION_CREATE"
642
- interaction = Interaction.make_interaction(self, data)
643
- dispatch(:interaction_create, interaction)
644
-
645
- dispatch(interaction.class.event_name, interaction)
646
- when "RESUMED"
647
- logger.info("Successfully resumed connection")
648
- @tasks << handle_heartbeat
649
- if shard
650
- dispatch(:shard_resumed, shard)
651
- else
652
- dispatch(:resumed)
653
- end
654
- when "GUILD_SCHEDULED_EVENT_CREATE"
655
- logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
656
- event = ScheduledEvent.new(self, data)
657
- guild.scheduled_events[data[:id]] = event
658
- dispatch(:scheduled_event_create, event)
659
- when "GUILD_SCHEDULED_EVENT_UPDATE"
660
- logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
661
- unless (event = guild.scheduled_events[data[:id]])
662
- logger.warn("Unknown scheduled event id #{data[:id]}, ignoring")
663
- end
664
- old = event.dup
665
- event.send(:_set_data, data)
666
- dispatch(:scheduled_event_update, old, event)
667
- if old.status == event.status
668
- dispatch(:scheduled_event_edit, old, event)
669
- else
670
- case event.status
671
- when :active
672
- dispatch(:scheduled_event_start, event)
673
- when :completed
674
- dispatch(:scheduled_event_end, event)
675
- end
676
- end
677
- when "GUILD_SCHEDULED_EVENT_DELETE"
678
- logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
679
- unless (event = guild.scheduled_events[data[:id]])
680
- logger.warn("Unknown scheduled event id #{data[:id]}, ignoring")
681
- end
682
- guild.scheduled_events.remove(data[:id])
683
- dispatch(:scheduled_event_delete, event)
684
- dispatch(:scheduled_event_cancel, event)
685
- when "GUILD_SCHEDULED_EVENT_USER_ADD"
686
- logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
687
- dispatch(:scheduled_event_user_add, ScheduledEventUserEvent.new(self, data))
688
- when "GUILD_SCHEDULED_EVENT_USER_REMOVE"
689
- logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
690
- dispatch(:scheduled_event_user_remove, ScheduledEventUserEvent.new(self, data))
691
- when "AUTO_MODERATION_ACTION_EXECUTION"
692
- dispatch(:auto_moderation_action_execution, AutoModerationActionExecutionEvent.new(self, data))
693
- when "AUTO_MODERATION_RULE_CREATE"
694
- dispatch(:auto_moderation_rule_create, AutoModRule.new(self, data))
695
- when "AUTO_MODERATION_RULE_UPDATE"
696
- dispatch(:auto_moderation_rule_update, AutoModRule.new(self, data))
697
- when "AUTO_MODERATION_RULE_DELETE"
698
- dispatch(:auto_moderation_rule_delete, AutoModRule.new(self, data))
699
- else
700
- if respond_to?("event_" + event_name.downcase)
701
- __send__("event_" + event_name.downcase, data)
702
- else
703
- logger.debug "Unhandled event: #{event_name}\n#{data.inspect}"
704
- end
705
- end
706
- end
707
-
708
- def ready
709
- Async do
710
- if @fetch_member
711
- logger.debug "Fetching members"
712
- barrier = Async::Barrier.new
713
-
714
- @guilds.each do |guild|
715
- barrier.async(parent: barrier) do
716
- guild.fetch_members
717
- end
718
- end
719
- barrier.wait
720
- end
721
- @ready = true
722
-
723
- if self.shard
724
- logger.info("Shard #{shard_id} is ready!")
725
- self.shard&.tap do |shard|
726
- if shard.next_shard
727
- dispatch(:shard_standby, shard)
728
- shard.next_shard.tap do |next_shard|
729
- logger.debug("Starting shard #{next_shard.id}")
730
- next_shard.start
731
- end
732
- else
733
- logger.info("All shards are ready!")
734
- dispatch(:standby)
735
- end
736
- end
737
- else
738
- logger.info("Client is ready!")
739
- dispatch(:standby)
740
- end
741
- end
742
- end
743
- end
744
-
745
- #
746
- # A class for connecting websocket with raw bytes data.
747
- # @private
748
- #
749
- class RawConnection < Async::WebSocket::Connection
750
- def initialize(*, **)
751
- super
752
- @closed = false
753
- end
754
-
755
- def inspect
756
- "<#{self.class.name} #{io.fileno}>"
757
- end
758
-
759
- def closed?
760
- @closed
761
- end
762
-
763
- def close
764
- super
765
- @closed = true
766
- rescue StandardError
767
- force_close
768
- end
769
-
770
- def force_close
771
- io.close
772
- @closed = true
773
- end
774
-
775
- def io
776
- @framer
777
- .instance_variable_get(:@stream)
778
- .instance_variable_get(:@io)
779
- .instance_variable_get(:@io)
780
- .instance_variable_get(:@io)
781
- end
782
-
783
- def parse(buffer)
784
- # noop
785
- buffer.to_s
786
- end
787
-
788
- def dump(object)
789
- # noop
790
- object.to_s
791
- end
792
- end
793
- end
794
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "async/http"
4
+ require "async/websocket"
5
+ require "async/barrier"
6
+ require "json"
7
+ require "zlib"
8
+
9
+ module Discorb
10
+ #
11
+ # A module for Discord Gateway.
12
+ # This module is internal use only.
13
+ #
14
+ module Gateway
15
+ #
16
+ # A module to handle gateway events.
17
+ #
18
+ module Handler
19
+ # @type instance: Discorb::Client
20
+
21
+ private
22
+
23
+ def connect_gateway(reconnect)
24
+ Async do
25
+ @mutex["gateway_#{shard_id}"] ||= Mutex.new
26
+ @mutex["gateway_#{shard_id}"].synchronize do
27
+ if reconnect
28
+ logger.info "Reconnecting to gateway..."
29
+ else
30
+ logger.info "Connecting to gateway..."
31
+ end
32
+
33
+ @http = HTTP.new(self)
34
+ gateway_url =
35
+ if reconnect
36
+ @resume_gateway_url
37
+ else
38
+ _, gateway_response =
39
+ @http.request(Route.new("/gateway", "//gateway", :get)).wait
40
+ gateway_response[:url]
41
+ end
42
+ gateway_version = 10
43
+ endpoint =
44
+ Async::HTTP::Endpoint.parse(
45
+ "#{gateway_url}?v=#{gateway_version}&encoding=json&compress=zlib-stream&_=#{Time.now.to_i}",
46
+ alpn_protocols: Async::HTTP::Protocol::HTTP11.names
47
+ )
48
+ begin
49
+ reconnect_count = 0
50
+ begin
51
+ self.connection =
52
+ Async::WebSocket::Client.connect(
53
+ endpoint,
54
+ headers: [["User-Agent", Discorb::USER_AGENT]],
55
+ handler: RawConnection
56
+ )
57
+ rescue Async::WebSocket::ProtocolError => e
58
+ raise if reconnect_count > 3
59
+
60
+ logger.info "Failed to connect to gateway, retrying...: #{e.message}"
61
+ reconnect_count += 1
62
+ sleep 2**reconnect_count
63
+ retry
64
+ end
65
+ con = connection
66
+ zlib_stream = Zlib::Inflate.new(Zlib::MAX_WBITS)
67
+ buffer = +""
68
+ begin
69
+ while (message = con.read)
70
+ buffer << message
71
+ unless message.end_with?(
72
+ (+"\x00\x00\xff\xff").force_encoding("ASCII-8BIT")
73
+ )
74
+ next
75
+ end
76
+ begin
77
+ data = zlib_stream.inflate(buffer)
78
+ buffer = +""
79
+ message = JSON.parse(data, symbolize_names: true)
80
+ rescue JSON::ParserError
81
+ buffer = +""
82
+ logger.error "Received invalid JSON from gateway."
83
+ logger.debug data.to_s
84
+ else
85
+ handle_gateway(message, reconnect)
86
+ end
87
+ end
88
+ rescue Async::Wrapper::Cancelled,
89
+ OpenSSL::SSL::SSLError,
90
+ Async::Wrapper::WaitError,
91
+ IOError => e
92
+ next if @status == :closed
93
+
94
+ logger.info "Gateway connection closed, reconnecting: #{e.class}: #{e.message}"
95
+ con.force_close
96
+ connect_gateway(true)
97
+ next
98
+ end
99
+ rescue Protocol::WebSocket::ClosedError => e
100
+ @tasks.map(&:stop)
101
+ case e.code
102
+ when 4004
103
+ raise ClientError.new("Authentication failed"), cause: nil
104
+ when 4009
105
+ logger.info "Session timed out, reconnecting."
106
+ con.force_close
107
+ connect_gateway(true)
108
+ next
109
+ when 4014
110
+ raise ClientError.new("Disallowed intents were specified"),
111
+ cause: nil
112
+ when 4001, 4002, 4003, 4005, 4007
113
+ raise ClientError.new(<<~ERROR), cause: e
114
+ Disconnected from gateway, probably due to library issues.
115
+ #{e.message}
116
+
117
+ Please report this to the library issue tracker.
118
+ https://github.com/discorb-lib/discorb/issues
119
+ ERROR
120
+ when 1001
121
+ logger.info "Gateway closed with code 1001, reconnecting."
122
+ con.force_close
123
+ connect_gateway(true)
124
+ next
125
+ else
126
+ logger.error "Discord WebSocket closed with code #{e.code}."
127
+ logger.debug e.message.to_s
128
+ con.force_close
129
+ connect_gateway(false)
130
+ next
131
+ end
132
+ rescue StandardError => e
133
+ logger.error "Discord WebSocket error: #{e.full_message}"
134
+ con.force_close
135
+ connect_gateway(false)
136
+ next
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ def send_gateway(opcode, **value)
143
+ if @shards.any? && shard.nil?
144
+ @shards.map(&:connection)
145
+ else
146
+ [connection]
147
+ end.each do |con|
148
+ con.write({ op: opcode, d: value }.to_json)
149
+ con.flush
150
+ end
151
+ logger.debug "Sent message to fd #{connection.io.fileno}: #{
152
+ { op: opcode, d: value }.to_json.gsub(@token, "[Token]")
153
+ }"
154
+ end
155
+
156
+ def handle_gateway(payload, reconnect)
157
+ Async do |_task|
158
+ data = payload[:d]
159
+ @last_s = payload[:s] if payload[:s]
160
+ logger.debug "Received message with opcode #{payload[:op]} from gateway."
161
+ logger.debug payload.to_json.gsub(@token, "[Token]").to_s
162
+ case payload[:op]
163
+ when 10
164
+ @heartbeat_interval = data[:heartbeat_interval]
165
+ if reconnect
166
+ payload = { token: @token, session_id: session_id, seq: @last_s }
167
+ send_gateway(6, **payload)
168
+ else
169
+ payload = {
170
+ token: @token,
171
+ intents: @intents.value,
172
+ compress: false,
173
+ properties: {
174
+ "os" => RUBY_PLATFORM,
175
+ "browser" => "discorb",
176
+ "device" => "discorb"
177
+ }
178
+ }
179
+ payload[:shard] = [shard_id, @shard_count] if shard_id
180
+ payload[:presence] = @identify_presence if @identify_presence
181
+ send_gateway(2, **payload)
182
+ end
183
+ when 7
184
+ logger.info "Received opcode 7, stopping tasks"
185
+ @tasks.map(&:stop)
186
+ when 9
187
+ logger.warn "Received opcode 9, closed connection"
188
+ @tasks.map(&:stop)
189
+ if data
190
+ logger.info "Connection is resumable, reconnecting"
191
+ connection.force_close
192
+ connect_gateway(true)
193
+ else
194
+ logger.info "Connection is not resumable, reconnecting with opcode 2"
195
+ connection.force_close
196
+
197
+ sleep(2)
198
+ connect_gateway(false)
199
+ end
200
+ when 11
201
+ logger.debug "Received opcode 11"
202
+ @ping = Time.now.to_f - @heartbeat_before
203
+ when 0
204
+ handle_event(payload[:t], data)
205
+ end
206
+ end
207
+ end
208
+
209
+ def handle_heartbeat
210
+ Async do |_task|
211
+ interval = @heartbeat_interval
212
+ sleep(((interval / 1000.0) - 1) * rand)
213
+ loop do
214
+ unless connection.closed?
215
+ @heartbeat_before = Time.now.to_f
216
+ connection.write({ op: 1, d: @last_s }.to_json)
217
+ connection.flush
218
+ logger.debug "Sent opcode 1."
219
+ logger.debug "Waiting for heartbeat."
220
+ end
221
+ sleep((interval / 1000.0) - 1)
222
+ end
223
+ end
224
+ end
225
+
226
+ def handle_event(event_name, data)
227
+ if @wait_until_ready && !@ready &&
228
+ !%w[READY GUILD_CREATE].include?(event_name)
229
+ return(
230
+ logger.debug "Client isn't ready; event #{event_name} wasn't handled"
231
+ )
232
+ end
233
+
234
+ dispatch(:event_receive, event_name, data)
235
+ logger.debug "Handling event #{event_name}"
236
+ case event_name
237
+ when "READY"
238
+ @api_version = data[:v]
239
+ self.session_id = data[:session_id]
240
+ @user = ClientUser.new(self, data[:user])
241
+ @resume_gateway_url = data[:resume_gateway_url]
242
+ @uncached_guilds = data[:guilds].map { |g| g[:id] }
243
+ ready if (@uncached_guilds == []) || !@intents.guilds
244
+ dispatch(:ready)
245
+
246
+ @tasks << handle_heartbeat
247
+ when "GUILD_CREATE"
248
+ if @uncached_guilds.include?(data[:id])
249
+ Guild.new(self, data, true)
250
+ @uncached_guilds.delete(data[:id])
251
+ if @uncached_guilds == []
252
+ logger.debug "All guilds cached"
253
+ ready
254
+ end
255
+ elsif @guilds.has?(data[:id])
256
+ @guilds[data[:id]].send(:_set_data, data, true)
257
+ dispatch(:guild_available, guild)
258
+ else
259
+ guild = Guild.new(self, data, true)
260
+ dispatch(:guild_join, guild)
261
+ end
262
+ dispatch(:guild_create, @guilds[data[:id]])
263
+ when "MESSAGE_CREATE"
264
+ message = Message.new(self, data)
265
+ dispatch(:message, message)
266
+ when "GUILD_UPDATE"
267
+ if @guilds.has?(data[:id])
268
+ current = @guilds[data[:id]]
269
+ before =
270
+ Guild.new(
271
+ self,
272
+ current.instance_variable_get(:@data).merge(no_cache: true),
273
+ false
274
+ )
275
+ current.send(:_set_data, data, false)
276
+ dispatch(:guild_update, before, current)
277
+ else
278
+ logger.warn "Unknown guild id #{data[:id]}, ignoring"
279
+ end
280
+ when "GUILD_DELETE"
281
+ unless (guild = @guilds.delete(data[:id]))
282
+ return logger.warn "Unknown guild id #{data[:id]}, ignoring"
283
+ end
284
+
285
+ dispatch(:guild_delete, guild)
286
+ if data[:unavailable]
287
+ dispatch(:guild_destroy, guild)
288
+ else
289
+ dispatch(:guild_leave, guild)
290
+ end
291
+ when "GUILD_ROLE_CREATE"
292
+ unless (guild = @guilds[data[:guild_id]])
293
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
294
+ end
295
+
296
+ nr = Role.new(@client, guild, data[:role])
297
+ guild.roles[data[:role][:id]] = nr
298
+ dispatch(:role_create, nr)
299
+ when "GUILD_ROLE_UPDATE"
300
+ unless (guild = @guilds[data[:guild_id]])
301
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
302
+ end
303
+ unless guild.roles.has?(data[:role][:id])
304
+ return logger.warn "Unknown role id #{data[:role][:id]}, ignoring"
305
+ end
306
+
307
+ current = guild.roles[data[:role][:id]]
308
+ before =
309
+ Role.new(
310
+ @client,
311
+ guild,
312
+ current.instance_variable_get(:@data).update({ no_cache: true })
313
+ )
314
+ current.send(:_set_data, data[:role])
315
+ dispatch(:role_update, before, current)
316
+ when "GUILD_ROLE_DELETE"
317
+ unless (guild = @guilds[data[:guild_id]])
318
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
319
+ end
320
+ unless (role = guild.roles.delete(data[:role_id]))
321
+ return logger.warn "Unknown role id #{data[:role_id]}, ignoring"
322
+ end
323
+
324
+ dispatch(:role_delete, role)
325
+ when "CHANNEL_CREATE"
326
+ unless (guild = @guilds[data[:guild_id]])
327
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
328
+ end
329
+
330
+ nc = Channel.make_channel(self, data)
331
+ guild.channels[data[:id]] = nc
332
+
333
+ dispatch(:channel_create, nc)
334
+ when "CHANNEL_UPDATE"
335
+ unless (current = @channels[data[:id]])
336
+ return logger.warn "Unknown channel id #{data[:id]}, ignoring"
337
+ end
338
+
339
+ before =
340
+ Channel.make_channel(
341
+ self,
342
+ current.instance_variable_get(:@data),
343
+ no_cache: true
344
+ )
345
+ current.send(:_set_data, data)
346
+ dispatch(:channel_update, before, current)
347
+ when "CHANNEL_DELETE"
348
+ unless (channel = @channels.delete(data[:id]))
349
+ return logger.warn "Unknown channel id #{data[:id]}, ignoring"
350
+ end
351
+
352
+ @guilds[data[:guild_id]]&.channels&.delete(data[:id])
353
+ dispatch(:channel_delete, channel)
354
+ when "CHANNEL_PINS_UPDATE"
355
+ nil # do in MESSAGE_UPDATE
356
+ when "THREAD_CREATE"
357
+ thread = Channel.make_channel(self, data)
358
+
359
+ dispatch(:thread_create, thread)
360
+ if data.key?(:member)
361
+ dispatch(:thread_join, thread)
362
+ else
363
+ dispatch(:thread_new, thread)
364
+ end
365
+ when "THREAD_UPDATE"
366
+ unless (thread = @channels[data[:id]])
367
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring"
368
+ end
369
+
370
+ before =
371
+ Channel.make_channel(
372
+ self,
373
+ thread.instance_variable_get(:@data),
374
+ no_cache: true
375
+ )
376
+ thread.send(:_set_data, data)
377
+ dispatch(:thread_update, before, thread)
378
+ when "THREAD_DELETE"
379
+ unless (thread = @channels.delete(data[:id]))
380
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring"
381
+ end
382
+
383
+ @guilds[data[:guild_id]]&.channels&.delete(data[:id])
384
+ dispatch(:thread_delete, thread)
385
+ when "THREAD_LIST_SYNC"
386
+ data[:threads].each do |raw_thread|
387
+ thread =
388
+ Channel.make_channel(
389
+ self,
390
+ raw_thread.merge(
391
+ {
392
+ member:
393
+ raw_thread[:members].find do |m|
394
+ m[:id] == raw_thread[:id]
395
+ end
396
+ }
397
+ )
398
+ )
399
+ @channels[thread.id] = thread
400
+ end
401
+ when "THREAD_MEMBER_UPDATE"
402
+ unless (thread = @channels[data[:id]])
403
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring"
404
+ end
405
+
406
+ if (member = thread.members[data[:id]])
407
+ old =
408
+ ThreadChannel::Member.new(
409
+ self,
410
+ member.instance_variable_get(:@data),
411
+ data[:guild_id]
412
+ )
413
+ member.send(:_set_data, data)
414
+ else
415
+ old = nil
416
+ member = ThreadChannel::Member.new(self, data, data[:guild_id])
417
+ thread.members[data[:user_id]] = member
418
+ end
419
+ dispatch(:thread_member_update, thread, old, member)
420
+ when "THREAD_MEMBERS_UPDATE"
421
+ unless (thread = @channels[data[:id]])
422
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring"
423
+ end
424
+
425
+ thread.instance_variable_set(:@member_count, data[:member_count])
426
+ members = []
427
+ (data[:added_members] || []).each do |raw_member|
428
+ member =
429
+ ThreadChannel::Member.new(self, raw_member, data[:guild_id])
430
+ thread.members[member.id] = member
431
+ members << member
432
+ end
433
+ removed_members = []
434
+ (data[:removed_member_ids] || []).each do |id|
435
+ removed_members << thread.members.delete(id)
436
+ end
437
+ dispatch(:thread_members_update, thread, members, removed_members)
438
+ when "STAGE_INSTANCE_CREATE"
439
+ instance = StageInstance.new(self, data)
440
+ dispatch(:stage_instance_create, instance)
441
+ when "STAGE_INSTANCE_UPDATE"
442
+ unless (channel = @channels[data[:channel_id]])
443
+ return(
444
+ logger.warn "Unknown channel id #{data[:channel_id]} , ignoring"
445
+ )
446
+ end
447
+ unless (instance = channel.stage_instances[data[:id]])
448
+ return(
449
+ logger.warn "Unknown stage instance id #{data[:id]}, ignoring"
450
+ )
451
+ end
452
+
453
+ old =
454
+ StageInstance.new(
455
+ self,
456
+ instance.instance_variable_get(:@data),
457
+ no_cache: true
458
+ )
459
+ current.send(:_set_data, data)
460
+ dispatch(:stage_instance_update, old, current)
461
+ when "STAGE_INSTANCE_DELETE"
462
+ unless (channel = @channels[data[:channel_id]])
463
+ return(
464
+ logger.warn "Unknown channel id #{data[:channel_id]} , ignoring"
465
+ )
466
+ end
467
+ unless (instance = channel.stage_instances.delete(data[:id]))
468
+ return(
469
+ logger.warn "Unknown stage instance id #{data[:id]}, ignoring"
470
+ )
471
+ end
472
+
473
+ dispatch(:stage_instance_delete, instance)
474
+ when "GUILD_MEMBER_ADD"
475
+ unless (guild = @guilds[data[:guild_id]])
476
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
477
+ end
478
+
479
+ nm =
480
+ Member.new(
481
+ self,
482
+ data[:guild_id],
483
+ data[:user].update({ no_cache: true }),
484
+ data
485
+ )
486
+ guild.members[nm.id] = nm
487
+ dispatch(:member_add, nm)
488
+ when "GUILD_MEMBER_UPDATE"
489
+ unless (guild = @guilds[data[:guild_id]])
490
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
491
+ end
492
+ unless (nm = guild.members[data[:user][:id]])
493
+ return logger.warn "Unknown member id #{data[:user][:id]}, ignoring"
494
+ end
495
+
496
+ old =
497
+ Member.new(
498
+ self,
499
+ data[:guild_id],
500
+ data[:user],
501
+ data.update({ no_cache: true })
502
+ )
503
+ nm.send(:_set_data, data[:user], data)
504
+ dispatch(:member_update, old, nm)
505
+ when "GUILD_MEMBER_REMOVE"
506
+ unless (guild = @guilds[data[:guild_id]])
507
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
508
+ end
509
+ unless (member = guild.members.delete(data[:user][:id]))
510
+ return logger.warn "Unknown member id #{data[:user][:id]}, ignoring"
511
+ end
512
+
513
+ dispatch(:member_remove, member)
514
+ when "GUILD_BAN_ADD"
515
+ unless (guild = @guilds[data[:guild_id]])
516
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
517
+ end
518
+
519
+ user =
520
+ if @users.has? data[:user][:id]
521
+ @users[data[:user][:id]]
522
+ else
523
+ User.new(self, data[:user].update({ no_cache: true }))
524
+ end
525
+
526
+ dispatch(:guild_ban_add, guild, user)
527
+ when "GUILD_BAN_REMOVE"
528
+ unless (guild = @guilds[data[:guild_id]])
529
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
530
+ end
531
+
532
+ user =
533
+ if @users.has? data[:user][:id]
534
+ @users[data[:user][:id]]
535
+ else
536
+ User.new(self, data[:user].update({ no_cache: true }))
537
+ end
538
+
539
+ dispatch(:guild_ban_remove, guild, user)
540
+ when "GUILD_EMOJIS_UPDATE"
541
+ unless (guild = @guilds[data[:guild_id]])
542
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
543
+ end
544
+
545
+ before_emojis = guild.emojis.values.map(&:id).to_set
546
+ data[:emojis].each do |emoji|
547
+ guild.emojis[emoji[:id]] = CustomEmoji.new(self, guild, emoji)
548
+ end
549
+ deleted_emojis = before_emojis - guild.emojis.values.map(&:id).to_set
550
+ deleted_emojis.each { |emoji| guild.emojis.delete(emoji) }
551
+ when "GUILD_INTEGRATIONS_UPDATE"
552
+ dispatch(:guild_integrations_update, @guilds[data[:guild_id]])
553
+ when "INTEGRATION_CREATE"
554
+ dispatch(
555
+ :integration_create,
556
+ Integration.new(self, data, data[:guild_id])
557
+ )
558
+ when "INTEGRATION_UPDATE"
559
+ unless (guild = @guilds[data[:guild_id]])
560
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
561
+ end
562
+
563
+ integration = Integration.new(self, data, data[:guild_id])
564
+ dispatch(:integration_update, integration)
565
+ when "INTEGRATION_DELETE"
566
+ unless (guild = @guilds[data[:guild_id]])
567
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
568
+ end
569
+
570
+ dispatch(:integration_delete, IntegrationDeleteEvent.new(self, data))
571
+ when "WEBHOOKS_UPDATE"
572
+ dispatch(:webhooks_update, WebhooksUpdateEvent.new(self, data))
573
+ when "INVITE_CREATE"
574
+ dispatch(:invite_create, Invite.new(self, data, true))
575
+ when "INVITE_DELETE"
576
+ dispatch(:invite_delete, InviteDeleteEvent.new(self, data))
577
+ when "VOICE_STATE_UPDATE"
578
+ unless (guild = @guilds[data[:guild_id]])
579
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
580
+ end
581
+
582
+ current = guild.voice_states[data[:user_id]]
583
+ if current.nil?
584
+ old = nil
585
+ current = VoiceState.new(self, data)
586
+ guild.voice_states[data[:user_id]] = current
587
+ else
588
+ guild.voice_states.remove(data[:user_id]) if data[:channel_id].nil?
589
+ old = VoiceState.new(self, current.instance_variable_get(:@data))
590
+ current.send(:_set_data, data)
591
+ end
592
+ dispatch(:voice_state_update, old, current)
593
+ if old&.channel != current&.channel
594
+ dispatch(:voice_channel_update, old, current)
595
+ case [old&.channel.nil?, current&.channel.nil?]
596
+ when [true, false]
597
+ dispatch(:voice_channel_connect, current)
598
+ when [false, true]
599
+ dispatch(:voice_channel_disconnect, old)
600
+ when [false, false]
601
+ dispatch(:voice_channel_move, old, current)
602
+ end
603
+ end
604
+ if old&.mute? != current&.mute?
605
+ dispatch(:voice_mute_update, old, current)
606
+ case [old&.mute?, current&.mute?]
607
+ when [false, true]
608
+ dispatch(:voice_mute_enable, current)
609
+ when [true, false]
610
+ dispatch(:voice_mute_disable, old)
611
+ end
612
+ end
613
+ if old&.deaf? != current&.deaf?
614
+ dispatch(:voice_deaf_update, old, current)
615
+ case [old&.deaf?, current&.deaf?]
616
+ when [false, true]
617
+ dispatch(:voice_deaf_enable, current)
618
+ when [true, false]
619
+ dispatch(:voice_deaf_disable, old)
620
+ end
621
+ end
622
+ if old&.self_mute? != current&.self_mute?
623
+ dispatch(:voice_self_mute_update, old, current)
624
+ case [old&.self_mute?, current&.self_mute?]
625
+ when [false, true]
626
+ dispatch(:voice_self_mute_enable, current)
627
+ when [true, false]
628
+ dispatch(:voice_self_mute_disable, old)
629
+ end
630
+ end
631
+ if old&.self_deaf? != current&.self_deaf?
632
+ dispatch(:voice_self_deaf_update, old, current)
633
+ case [old&.self_deaf?, current&.self_deaf?]
634
+ when [false, true]
635
+ dispatch(:voice_self_deaf_enable, current)
636
+ when [true, false]
637
+ dispatch(:voice_self_deaf_disable, old)
638
+ end
639
+ end
640
+ if old&.server_mute? != current&.server_mute?
641
+ dispatch(:voice_server_mute_update, old, current)
642
+ case [old&.server_mute?, current&.server_mute?]
643
+ when [false, true]
644
+ dispatch(:voice_server_mute_enable, current)
645
+ when [true, false]
646
+ dispatch(:voice_server_mute_disable, old)
647
+ end
648
+ end
649
+ if old&.server_deaf? != current&.server_deaf?
650
+ dispatch(:voice_server_deaf_update, old, current)
651
+ case [old&.server_deaf?, current&.server_deaf?]
652
+ when [false, true]
653
+ dispatch(:voice_server_deaf_enable, current)
654
+ when [true, false]
655
+ dispatch(:voice_server_deaf_disable, old)
656
+ end
657
+ end
658
+ if old&.video? != current&.video?
659
+ dispatch(:voice_video_update, old, current)
660
+ case [old&.video?, current&.video?]
661
+ when [false, true]
662
+ dispatch(:voice_video_start, current)
663
+ when [true, false]
664
+ dispatch(:voice_video_end, old)
665
+ end
666
+ end
667
+ if old&.stream? != current&.stream?
668
+ dispatch(:voice_stream_update, old, current)
669
+ case [old&.stream?, current&.stream?]
670
+ when [false, true]
671
+ dispatch(:voice_stream_start, current)
672
+ when [true, false]
673
+ dispatch(:voice_stream_end, old)
674
+ end
675
+ end
676
+ when "PRESENCE_UPDATE"
677
+ unless (guild = @guilds[data[:guild_id]])
678
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring"
679
+ end
680
+
681
+ guild.presences[data[:user][:id]] = Presence.new(self, data)
682
+ when "MESSAGE_UPDATE"
683
+ if (message = @messages[data[:id]])
684
+ before =
685
+ Message.new(
686
+ self,
687
+ message.instance_variable_get(:@data),
688
+ no_cache: true
689
+ )
690
+ message.send(
691
+ :_set_data,
692
+ message.instance_variable_get(:@data).merge(data)
693
+ )
694
+ else
695
+ before = nil
696
+ message = nil
697
+ end
698
+ if data[:edited_timestamp].nil?
699
+ if message.nil?
700
+ nil
701
+ elsif message.pinned?
702
+ message.instance_variable_set(:@pinned, false)
703
+ else
704
+ message.instance_variable_set(:@pinned, true)
705
+ end
706
+ dispatch(
707
+ :message_pin_update,
708
+ MessagePinEvent.new(self, data, message)
709
+ )
710
+ else
711
+ dispatch(
712
+ :message_update,
713
+ MessageUpdateEvent.new(self, data, before, current)
714
+ )
715
+ end
716
+ when "MESSAGE_DELETE"
717
+ if (message = @messages[data[:id]])
718
+ message.instance_variable_set(:@deleted, true)
719
+ end
720
+
721
+ dispatch(
722
+ :message_delete_id,
723
+ Snowflake.new(data[:id]),
724
+ channels[data[:channel_id]],
725
+ data[:guild_id] && guilds[data[:guild_id]]
726
+ )
727
+ dispatch(
728
+ :message_delete,
729
+ message,
730
+ channels[data[:channel_id]],
731
+ data[:guild_id] && guilds[data[:guild_id]]
732
+ )
733
+ when "MESSAGE_DELETE_BULK"
734
+ messages = []
735
+ data[:ids].each do |id|
736
+ if (message = @messages[id])
737
+ message.instance_variable_set(:@deleted, true)
738
+ messages.push(message)
739
+ else
740
+ messages.push(UnknownDeleteBulkMessage.new(self, id, data))
741
+ end
742
+ end
743
+ dispatch(:message_delete_bulk, messages)
744
+ when "MESSAGE_REACTION_ADD"
745
+ if (target_message = @messages[data[:message_id]])
746
+ if (
747
+ target_reaction =
748
+ target_message.reactions.find do |r|
749
+ if r.emoji.is_a?(UnicodeEmoji)
750
+ r.emoji.value == data[:emoji][:name]
751
+ else
752
+ r.emoji.id == data[:emoji][:id]
753
+ end
754
+ end
755
+ )
756
+ target_reaction.instance_variable_set(
757
+ :@count,
758
+ target_reaction.count + 1
759
+ )
760
+ else
761
+ target_message.reactions << Reaction.new(
762
+ target_message,
763
+ {
764
+ count: 1,
765
+ me: @user.id == data[:user_id],
766
+ emoji: data[:emoji]
767
+ }
768
+ )
769
+ end
770
+ end
771
+ dispatch(:reaction_add, ReactionEvent.new(self, data))
772
+ when "MESSAGE_REACTION_REMOVE"
773
+ if (target_message = @messages[data[:message_id]]) &&
774
+ (
775
+ target_reaction =
776
+ target_message.reactions.find do |r|
777
+ if data[:emoji][:id].nil?
778
+ r.emoji.name == data[:emoji][:name]
779
+ else
780
+ r.emoji.id == data[:emoji][:id]
781
+ end
782
+ end
783
+ )
784
+ target_reaction.instance_variable_set(
785
+ :@count,
786
+ target_reaction.count - 1
787
+ )
788
+ if target_reaction.count.zero?
789
+ target_message.reactions.delete(target_reaction)
790
+ end
791
+ end
792
+ dispatch(:reaction_remove, ReactionEvent.new(self, data))
793
+ when "MESSAGE_REACTION_REMOVE_ALL"
794
+ if (target_message = @messages[data[:message_id]])
795
+ target_message.reactions = []
796
+ end
797
+ dispatch(:reaction_remove_all, ReactionRemoveAllEvent.new(self, data))
798
+ when "MESSAGE_REACTION_REMOVE_EMOJI"
799
+ if (target_message = @messages[data[:message_id]]) &&
800
+ (
801
+ target_reaction =
802
+ target_message.reactions.find do |r|
803
+ if data[:emoji][:id].nil?
804
+ r.name == data[:emoji][:name]
805
+ else
806
+ r.id == data[:emoji][:id]
807
+ end
808
+ end
809
+ )
810
+ target_message.reactions.delete(target_reaction)
811
+ end
812
+ dispatch(
813
+ :reaction_remove_emoji,
814
+ ReactionRemoveEmojiEvent.new(self, data)
815
+ )
816
+ when "TYPING_START"
817
+ dispatch(:typing_start, TypingStartEvent.new(self, data))
818
+ when "INTERACTION_CREATE"
819
+ interaction = Interaction.make_interaction(self, data)
820
+ dispatch(:interaction_create, interaction)
821
+
822
+ dispatch(interaction.class.event_name, interaction)
823
+ when "RESUMED"
824
+ logger.info("Successfully resumed connection")
825
+ @tasks << handle_heartbeat
826
+ shard ? dispatch(:shard_resumed, shard) : dispatch(:resumed)
827
+ when "GUILD_SCHEDULED_EVENT_CREATE"
828
+ unless (guild = @guilds[data[:guild_id]])
829
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring")
830
+ end
831
+ event = ScheduledEvent.new(self, data)
832
+ guild.scheduled_events[data[:id]] = event
833
+ dispatch(:scheduled_event_create, event)
834
+ when "GUILD_SCHEDULED_EVENT_UPDATE"
835
+ unless (guild = @guilds[data[:guild_id]])
836
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring")
837
+ end
838
+ unless (event = guild.scheduled_events[data[:id]])
839
+ logger.warn("Unknown scheduled event id #{data[:id]}, ignoring")
840
+ end
841
+ old = event.dup
842
+ event.send(:_set_data, data)
843
+ dispatch(:scheduled_event_update, old, event)
844
+ if old.status == event.status
845
+ dispatch(:scheduled_event_edit, old, event)
846
+ else
847
+ case event.status
848
+ when :active
849
+ dispatch(:scheduled_event_start, event)
850
+ when :completed
851
+ dispatch(:scheduled_event_end, event)
852
+ end
853
+ end
854
+ when "GUILD_SCHEDULED_EVENT_DELETE"
855
+ unless (guild = @guilds[data[:guild_id]])
856
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring")
857
+ end
858
+ unless (event = guild.scheduled_events[data[:id]])
859
+ logger.warn("Unknown scheduled event id #{data[:id]}, ignoring")
860
+ end
861
+ guild.scheduled_events.remove(data[:id])
862
+ dispatch(:scheduled_event_delete, event)
863
+ dispatch(:scheduled_event_cancel, event)
864
+ when "GUILD_SCHEDULED_EVENT_USER_ADD"
865
+ unless (guild = @guilds[data[:guild_id]])
866
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring")
867
+ end
868
+ dispatch(
869
+ :scheduled_event_user_add,
870
+ ScheduledEventUserEvent.new(self, data)
871
+ )
872
+ when "GUILD_SCHEDULED_EVENT_USER_REMOVE"
873
+ unless (guild = @guilds[data[:guild_id]])
874
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring")
875
+ end
876
+ dispatch(
877
+ :scheduled_event_user_remove,
878
+ ScheduledEventUserEvent.new(self, data)
879
+ )
880
+ when "AUTO_MODERATION_ACTION_EXECUTION"
881
+ dispatch(
882
+ :auto_moderation_action_execution,
883
+ AutoModerationActionExecutionEvent.new(self, data)
884
+ )
885
+ when "AUTO_MODERATION_RULE_CREATE"
886
+ dispatch(:auto_moderation_rule_create, AutoModRule.new(self, data))
887
+ when "AUTO_MODERATION_RULE_UPDATE"
888
+ dispatch(:auto_moderation_rule_update, AutoModRule.new(self, data))
889
+ when "AUTO_MODERATION_RULE_DELETE"
890
+ dispatch(:auto_moderation_rule_delete, AutoModRule.new(self, data))
891
+ else
892
+ if respond_to?("event_#{event_name.downcase}")
893
+ __send__("event_#{event_name.downcase}", data)
894
+ else
895
+ logger.debug "Unhandled event: #{event_name}\n#{data.inspect}"
896
+ end
897
+ end
898
+ end
899
+
900
+ def ready
901
+ Async do
902
+ if @fetch_member
903
+ logger.debug "Fetching members"
904
+ barrier = Async::Barrier.new
905
+
906
+ @guilds.each do |guild|
907
+ barrier.async(parent: barrier) { guild.fetch_members }
908
+ end
909
+ barrier.wait
910
+ end
911
+ @ready = true
912
+
913
+ if shard
914
+ logger.info("Shard #{shard_id} is ready!")
915
+ shard&.tap do |shard|
916
+ if shard.next_shard
917
+ dispatch(:shard_standby, shard)
918
+ shard.next_shard.tap do |next_shard|
919
+ logger.debug("Starting shard #{next_shard.id}")
920
+ next_shard.start
921
+ end
922
+ else
923
+ logger.info("All shards are ready!")
924
+ dispatch(:standby)
925
+ end
926
+ end
927
+ else
928
+ logger.info("Client is ready!")
929
+ dispatch(:standby)
930
+ end
931
+ end
932
+ end
933
+ end
934
+
935
+ #
936
+ # A class for connecting websocket with raw bytes data.
937
+ # @private
938
+ #
939
+ class RawConnection < Async::WebSocket::Connection
940
+ def initialize(*, **)
941
+ super
942
+ @closed = false
943
+ end
944
+
945
+ def inspect
946
+ "<#{self.class.name} #{io.fileno}>"
947
+ end
948
+
949
+ def closed?
950
+ @closed
951
+ end
952
+
953
+ def close
954
+ super
955
+ @closed = true
956
+ rescue StandardError
957
+ force_close
958
+ end
959
+
960
+ def force_close
961
+ io.close
962
+ @closed = true
963
+ end
964
+
965
+ def io
966
+ @framer
967
+ .instance_variable_get(:@stream)
968
+ .instance_variable_get(:@io)
969
+ .instance_variable_get(:@io)
970
+ .instance_variable_get(:@io)
971
+ end
972
+
973
+ def parse(buffer)
974
+ # noop
975
+ buffer.to_s
976
+ end
977
+
978
+ def dump(object)
979
+ # noop
980
+ object.to_s
981
+ end
982
+ end
983
+ end
984
+ end