discorb 0.18.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
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