discorb 0.14.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build_version.yml +1 -1
  3. data/.github/workflows/lint-push.yml +4 -2
  4. data/.rubocop.yml +6 -2
  5. data/Changelog.md +27 -0
  6. data/Rakefile +6 -0
  7. data/docs/events.md +71 -5
  8. data/docs/faq.md +8 -8
  9. data/examples/simple/rolepanel.rb +1 -1
  10. data/examples/simple/shard.rb +17 -0
  11. data/lib/discorb/allowed_mentions.rb +7 -0
  12. data/lib/discorb/app_command/command.rb +64 -2
  13. data/lib/discorb/app_command/handler.rb +1 -1
  14. data/lib/discorb/application.rb +16 -7
  15. data/lib/discorb/asset.rb +9 -0
  16. data/lib/discorb/{file.rb → attachment.rb} +55 -33
  17. data/lib/discorb/audit_logs.rb +42 -4
  18. data/lib/discorb/channel.rb +47 -12
  19. data/lib/discorb/client.rb +135 -51
  20. data/lib/discorb/common.rb +18 -4
  21. data/lib/discorb/components/button.rb +5 -6
  22. data/lib/discorb/components/select_menu.rb +2 -16
  23. data/lib/discorb/dictionary.rb +2 -0
  24. data/lib/discorb/embed.rb +71 -38
  25. data/lib/discorb/emoji.rb +29 -2
  26. data/lib/discorb/emoji_table.rb +1 -1
  27. data/lib/discorb/error.rb +7 -1
  28. data/lib/discorb/event.rb +27 -17
  29. data/lib/discorb/exe/new.rb +5 -5
  30. data/lib/discorb/exe/run.rb +1 -15
  31. data/lib/discorb/gateway.rb +262 -161
  32. data/lib/discorb/gateway_requests.rb +4 -7
  33. data/lib/discorb/guild.rb +67 -33
  34. data/lib/discorb/guild_template.rb +24 -3
  35. data/lib/discorb/http.rb +25 -3
  36. data/lib/discorb/integration.rb +23 -8
  37. data/lib/discorb/intents.rb +15 -10
  38. data/lib/discorb/interaction/autocomplete.rb +4 -4
  39. data/lib/discorb/interaction/command.rb +34 -5
  40. data/lib/discorb/interaction/components.rb +15 -2
  41. data/lib/discorb/interaction/response.rb +12 -0
  42. data/lib/discorb/interaction/root.rb +16 -1
  43. data/lib/discorb/invite.rb +11 -7
  44. data/lib/discorb/member.rb +21 -0
  45. data/lib/discorb/message.rb +59 -3
  46. data/lib/discorb/message_meta.rb +36 -55
  47. data/lib/discorb/modules.rb +38 -14
  48. data/lib/discorb/permission.rb +14 -5
  49. data/lib/discorb/presence.rb +41 -8
  50. data/lib/discorb/rate_limit.rb +7 -2
  51. data/lib/discorb/reaction.rb +6 -0
  52. data/lib/discorb/role.rb +12 -0
  53. data/lib/discorb/shard.rb +74 -0
  54. data/lib/discorb/sticker.rb +22 -14
  55. data/lib/discorb/user.rb +11 -0
  56. data/lib/discorb/voice_state.rb +20 -2
  57. data/lib/discorb/webhook.rb +53 -2
  58. data/lib/discorb.rb +5 -3
  59. data/sig/discorb.rbs +7234 -6714
  60. metadata +5 -4
  61. data/lib/discorb/log.rb +0 -81
@@ -14,12 +14,22 @@ module Discorb
14
14
  module Gateway
15
15
  #
16
16
  # Represents an event.
17
+ # @abstract
17
18
  #
18
19
  class GatewayEvent
20
+ #
21
+ # Initializes a new instance of the GatewayEvent class.
19
22
  # @private
23
+ #
24
+ # @param [Hash] data The data of the event.
25
+ #
20
26
  def initialize(data)
21
27
  @data = data
22
28
  end
29
+
30
+ def inspect
31
+ "#<#{self.class}>"
32
+ end
23
33
  end
24
34
 
25
35
  #
@@ -61,7 +71,13 @@ module Discorb
61
71
  alias reactor fired_by
62
72
  alias from fired_by
63
73
 
74
+ #
75
+ # Initializes a new instance of the ReactionEvent class.
64
76
  # @private
77
+ #
78
+ # @param [Discorb::Client] client The client that instantiated the object.
79
+ # @param [Hash] data The data of the event.
80
+ #
65
81
  def initialize(client, data)
66
82
  @client = client
67
83
  @data = data
@@ -121,7 +137,14 @@ module Discorb
121
137
  # @macro client_cache
122
138
  # @return [Discorb::User] The user associated with the integration.
123
139
 
140
+ #
141
+ # Initialize a new instance of the IntegrationDeleteEvent class.
124
142
  # @private
143
+ #
144
+ #
145
+ # @param [Hash] data The data of the event.
146
+ #
147
+ #
125
148
  def initialize(_client, data)
126
149
  @id = Snowflake.new(data[:id])
127
150
  @guild_id = data[:guild_id]
@@ -157,7 +180,13 @@ module Discorb
157
180
  # @return [Discorb::Message] The message the reaction was sent in.
158
181
  attr_reader :message
159
182
 
183
+ #
184
+ # Initialize a new instance of the ReactionRemoveAllEvent class.
160
185
  # @private
186
+ #
187
+ # @param [Discorb::Client] client The client that instantiated the object.
188
+ # @param [Hash] data The data of the event.
189
+ #
161
190
  def initialize(client, data)
162
191
  @client = client
163
192
  @data = data
@@ -207,7 +236,13 @@ module Discorb
207
236
  # @return [Discorb::UnicodeEmoji, Discorb::PartialEmoji] The emoji that was reacted with.
208
237
  attr_reader :emoji
209
238
 
239
+ #
240
+ # Initialize a new instance of the ReactionRemoveEmojiEvent class.
210
241
  # @private
242
+ #
243
+ # @param [Discorb::Client] client The client that instantiated the object.
244
+ # @param [Hash] data The data of the event.
245
+ #
211
246
  def initialize(client, data)
212
247
  @client = client
213
248
  @data = data
@@ -246,7 +281,13 @@ module Discorb
246
281
  attr_reader :guild
247
282
  # @return [Discorb::ScheduledEvent] The scheduled event.
248
283
  attr_reader :scheduled_event
284
+ #
285
+ # Initialize a new instance of the ScheduledEventUserEvent class.
249
286
  # @private
287
+ #
288
+ # @param [Discorb::Client] client The client that instantiated the object.
289
+ # @param [Hash] data The data of the event.
290
+ #
250
291
  def initialize(client, data)
251
292
  @client = client
252
293
  @scheduled_event_id = Snowflake.new(data[:scheduled_event_id])
@@ -305,8 +346,8 @@ module Discorb
305
346
  @timestamp = Time.iso8601(data[:edited_timestamp])
306
347
  @mention_everyone = data[:mention_everyone]
307
348
  @mention_roles = data[:mention_roles].map { |r| guild.roles[r] } if data.key?(:mention_roles)
308
- @attachments = data[:attachments].map { |a| Attachment.new(a) } if data.key?(:attachments)
309
- @embeds = data[:embeds] ? data[:embeds].map { |e| Embed.new(data: e) } : [] if data.key?(:embeds)
349
+ @attachments = data[:attachments].map { |a| Attachment.from_hash(a) } if data.key?(:attachments)
350
+ @embeds = data[:embeds] ? data[:embeds].map { |e| Embed.from_hash(e) } : [] if data.key?(:embeds)
310
351
  end
311
352
 
312
353
  def channel
@@ -342,7 +383,13 @@ module Discorb
342
383
  # @macro client_cache
343
384
  # @return [Discorb::Guild] The guild the message was sent in.
344
385
 
386
+ #
387
+ # Initialize a new instance of the UnknownDeleteBulkMessage class.
345
388
  # @private
389
+ #
390
+ # @param [Discorb::Client] client The client that instantiated the object.
391
+ # @param [Hash] data The data of the event.
392
+ #
346
393
  def initialize(client, id, data)
347
394
  @client = client
348
395
  @id = id
@@ -374,7 +421,13 @@ module Discorb
374
421
  # @macro client_cache
375
422
  # @return [Discorb::Guild] The guild the message was sent in.
376
423
 
424
+ #
425
+ # Initialize a new instance of the InviteDeleteEvent class.
377
426
  # @private
427
+ #
428
+ # @param [Discorb::Client] client The client that instantiated the object.
429
+ # @param [Hash] data The data of the event.
430
+ #
378
431
  def initialize(client, data)
379
432
  @client = client
380
433
  @data = data
@@ -392,21 +445,6 @@ module Discorb
392
445
  end
393
446
  end
394
447
 
395
- #
396
- # Represents a `GUILD_INTEGRATIONS_UPDATE` event.
397
- #
398
- class GuildIntegrationsUpdateEvent < GatewayEvent
399
- def initialize(client, data)
400
- @client = client
401
- @data = data
402
- @guild_id = Snowflake.new(data[:guild_id])
403
- end
404
-
405
- def guild
406
- @client.guilds[@guild_id]
407
- end
408
- end
409
-
410
448
  #
411
449
  # Represents a `TYPING_START` event.
412
450
  #
@@ -430,7 +468,13 @@ module Discorb
430
468
  # @macro client_cache
431
469
  # @return [Discorb::Member, Discorb::User] The member or user that started typing.
432
470
 
471
+ #
472
+ # Initialize a new instance of the TypingStartEvent class.
433
473
  # @private
474
+ #
475
+ # @param [Discorb::Client] client The client that instantiated the object.
476
+ # @param [Hash] data The data of the event.
477
+ #
434
478
  def initialize(client, data)
435
479
  @client = client
436
480
  @data = data
@@ -507,7 +551,13 @@ module Discorb
507
551
  # @macro client_cache
508
552
  # @return [Discorb::Guild] The guild where the webhook was updated.
509
553
 
554
+ #
555
+ # Initialize a new instance of the WebhooksUpdateEvent class.
510
556
  # @private
557
+ #
558
+ # @param [Discorb::Client] client The client that instantiated the object.
559
+ # @param [Hash] data The data of the event.
560
+ #
511
561
  def initialize(client, data)
512
562
  @client = client
513
563
  @data = data
@@ -531,109 +581,129 @@ module Discorb
531
581
  private
532
582
 
533
583
  def connect_gateway(reconnect)
534
- if reconnect
535
- @log.info "Reconnecting to gateway..."
536
- else
537
- @log.info "Connecting to gateway..."
538
- end
539
584
  Async do
540
- if @connection && !@connection.closed?
541
- Async do
542
- @connection.close
585
+ @mutex["gateway_#{shard_id}"] ||= Mutex.new
586
+ @mutex["gateway_#{shard_id}"].synchronize do
587
+ if reconnect
588
+ logger.info "Reconnecting to gateway..."
589
+ else
590
+ logger.info "Connecting to gateway..."
543
591
  end
544
- end
545
- @http = HTTP.new(self)
546
- _, gateway_response = @http.request(Route.new("/gateway", "//gateway", :get)).wait
547
- gateway_url = gateway_response[:url]
548
- endpoint = Async::HTTP::Endpoint.parse("#{gateway_url}?v=9&encoding=json&compress=zlib-stream",
549
- alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
550
- begin
551
- @connection = Async::WebSocket::Client.connect(endpoint, headers: [["User-Agent", Discorb::USER_AGENT]], handler: RawConnection)
552
- @zlib_stream = Zlib::Inflate.new(Zlib::MAX_WBITS)
553
- buffer = +""
592
+
593
+ @http = HTTP.new(self)
594
+ _, gateway_response = @http.request(Route.new("/gateway", "//gateway", :get)).wait
595
+ gateway_url = gateway_response[:url]
596
+ gateway_version = if @intents.to_h[:message_content].nil?
597
+ warn "message_content intent not set, using gateway version 9. You should specify `message_content` intent for preventing unexpected changes in the future."
598
+ 9
599
+ else
600
+ 10
601
+ end
602
+ endpoint = Async::HTTP::Endpoint.parse(
603
+ "#{gateway_url}?v=#{gateway_version}&encoding=json&compress=zlib-stream&_=#{Time.now.to_i}",
604
+ alpn_protocols: Async::HTTP::Protocol::HTTP11.names,
605
+ )
554
606
  begin
555
- while (message = @connection.read)
556
- buffer << message
557
- if message.end_with?((+"\x00\x00\xff\xff").force_encoding("ASCII-8BIT"))
558
- begin
559
- data = @zlib_stream.inflate(buffer)
560
- buffer = +""
561
- message = JSON.parse(data, symbolize_names: true)
562
- rescue JSON::ParserError
563
- buffer = +""
564
- @log.error "Received invalid JSON from gateway."
565
- @log.debug "#{data}"
566
- else
567
- handle_gateway(message, reconnect)
607
+ self.connection = Async::WebSocket::Client.connect(endpoint, headers: [["User-Agent", Discorb::USER_AGENT]], handler: RawConnection)
608
+ zlib_stream = Zlib::Inflate.new(Zlib::MAX_WBITS)
609
+ buffer = +""
610
+ begin
611
+ while (message = connection.read)
612
+ buffer << message
613
+ if message.end_with?((+"\x00\x00\xff\xff").force_encoding("ASCII-8BIT"))
614
+ begin
615
+ data = zlib_stream.inflate(buffer)
616
+ buffer = +""
617
+ message = JSON.parse(data, symbolize_names: true)
618
+ rescue JSON::ParserError
619
+ buffer = +""
620
+ logger.error "Received invalid JSON from gateway."
621
+ logger.debug "#{data}"
622
+ else
623
+ handle_gateway(message, reconnect)
624
+ end
568
625
  end
569
626
  end
627
+ rescue Async::Wrapper::Cancelled,
628
+ OpenSSL::SSL::SSLError,
629
+ Async::Wrapper::WaitError,
630
+ EOFError,
631
+ Errno::EPIPE,
632
+ Errno::ECONNRESET,
633
+ IOError => e
634
+ next if @status == :closed
635
+ logger.error "Gateway connection closed accidentally: #{e.class}: #{e.message}"
636
+ connection.force_close
637
+ connect_gateway(true)
638
+ next
570
639
  end
571
- rescue Async::Wrapper::Cancelled,
572
- OpenSSL::SSL::SSLError,
573
- Async::Wrapper::WaitError,
574
- EOFError,
575
- Errno::EPIPE,
576
- Errno::ECONNRESET,
577
- IOError => e
578
- @log.error "Gateway connection closed accidentally: #{e.class}: #{e.message}"
579
- @connection.force_close
580
- connect_gateway(true)
581
- else # should never happen
582
- connect_gateway(true)
583
- end
584
- rescue Protocol::WebSocket::ClosedError => e
585
- @tasks.map(&:stop)
586
- case e.code
587
- when 4004
588
- raise ClientError.new("Authentication failed"), cause: nil
589
- when 4009
590
- @log.info "Session timed out, reconnecting."
591
- connect_gateway(true)
592
- when 4014
593
- raise ClientError.new("Disallowed intents were specified"), cause: nil
594
- when 4002, 4003, 4005, 4007
595
- raise ClientError.new(<<~ERROR), cause: e
596
- Disconnected from gateway, probably due to library issues.
597
- #{e.message}
598
-
599
- Please report this to the library issue tracker.
600
- https://github.com/discorb-lib/discorb/issues
601
- ERROR
602
- when 1001
603
- @log.info "Gateway closed with code 1001, reconnecting."
604
- connect_gateway(true)
605
- else
606
- @log.error "Discord WebSocket closed with code #{e.code}."
607
- @log.debug "#{e.message}"
640
+ rescue Protocol::WebSocket::ClosedError => e
641
+ @tasks.map(&:stop)
642
+ case e.code
643
+ when 4004
644
+ raise ClientError.new("Authentication failed"), cause: nil
645
+ when 4009
646
+ logger.info "Session timed out, reconnecting."
647
+ connection.force_close
648
+ connect_gateway(true)
649
+ next
650
+ when 4014
651
+ raise ClientError.new("Disallowed intents were specified"), cause: nil
652
+ when 4001, 4002, 4003, 4005, 4007
653
+ raise ClientError.new(<<~ERROR), cause: e
654
+ Disconnected from gateway, probably due to library issues.
655
+ #{e.message}
656
+
657
+ Please report this to the library issue tracker.
658
+ https://github.com/discorb-lib/discorb/issues
659
+ ERROR
660
+ when 1001
661
+ logger.info "Gateway closed with code 1001, reconnecting."
662
+ connection.force_close
663
+ connect_gateway(true)
664
+ next
665
+ else
666
+ logger.error "Discord WebSocket closed with code #{e.code}."
667
+ logger.debug "#{e.message}"
668
+ connection.force_close
669
+ connect_gateway(false)
670
+ next
671
+ end
672
+ rescue StandardError => e
673
+ logger.error "Discord WebSocket error: #{e.full_message}"
674
+ connection.force_close
608
675
  connect_gateway(false)
676
+ next
609
677
  end
610
- rescue StandardError => e
611
- @log.error "Discord WebSocket error: #{e.full_message}"
612
- @connection.force_close
613
- connect_gateway(false)
614
678
  end
615
679
  end
616
680
  end
617
681
 
618
682
  def send_gateway(opcode, **value)
619
- @connection.write({ op: opcode, d: value }.to_json)
620
- @connection.flush
621
- @log.debug "Sent message #{{ op: opcode, d: value }.to_json.gsub(@token, "[Token]")}"
683
+ if @shards.any? && shard.nil?
684
+ @shards.map(&:connection)
685
+ else
686
+ [connection]
687
+ end.each do |con|
688
+ con.write({ op: opcode, d: value }.to_json)
689
+ con.flush
690
+ end
691
+ logger.debug "Sent message to fd #{connection.io.fileno}: #{{ op: opcode, d: value }.to_json.gsub(@token, "[Token]")}"
622
692
  end
623
693
 
624
694
  def handle_gateway(payload, reconnect)
625
695
  Async do |_task|
626
696
  data = payload[:d]
627
697
  @last_s = payload[:s] if payload[:s]
628
- @log.debug "Received message with opcode #{payload[:op]} from gateway:"
629
- @log.debug "#{payload.to_json.gsub(@token, "[Token]")}"
698
+ logger.debug "Received message with opcode #{payload[:op]} from gateway."
699
+ logger.debug "#{payload.to_json.gsub(@token, "[Token]")}"
630
700
  case payload[:op]
631
701
  when 10
632
702
  @heartbeat_interval = data[:heartbeat_interval]
633
703
  if reconnect
634
704
  payload = {
635
705
  token: @token,
636
- session_id: @session_id,
706
+ session_id: session_id,
637
707
  seq: @last_s,
638
708
  }
639
709
  send_gateway(6, **payload)
@@ -644,27 +714,29 @@ module Discorb
644
714
  compress: false,
645
715
  properties: { "$os" => RUBY_PLATFORM, "$browser" => "discorb", "$device" => "discorb" },
646
716
  }
717
+ payload[:shard] = [shard_id, @shard_count] if shard_id
647
718
  payload[:presence] = @identify_presence if @identify_presence
648
719
  send_gateway(2, **payload)
649
720
  end
650
721
  when 7
651
- @log.info "Received opcode 7, stopping tasks"
722
+ logger.info "Received opcode 7, stopping tasks"
652
723
  @tasks.map(&:stop)
653
724
  when 9
654
- @log.warn "Received opcode 9, closed connection"
725
+ logger.warn "Received opcode 9, closed connection"
655
726
  @tasks.map(&:stop)
656
727
  if data
657
- @log.info "Connection is resumable, reconnecting"
658
- @connection.close
728
+ logger.info "Connection is resumable, reconnecting"
729
+ connection.force_close
659
730
  connect_gateway(true)
660
731
  else
661
- @log.info "Connection is not resumable, reconnecting with opcode 2"
662
- @connection.close
732
+ logger.info "Connection is not resumable, reconnecting with opcode 2"
733
+ connection.force_close
734
+
663
735
  sleep(2)
664
736
  connect_gateway(false)
665
737
  end
666
738
  when 11
667
- @log.debug "Received opcode 11"
739
+ logger.debug "Received opcode 11"
668
740
  @ping = Time.now.to_f - @heartbeat_before
669
741
  when 0
670
742
  handle_event(payload[:t], data)
@@ -677,12 +749,12 @@ module Discorb
677
749
  interval = @heartbeat_interval
678
750
  sleep((interval / 1000.0 - 1) * rand)
679
751
  loop do
680
- unless @connection.closed?
752
+ unless connection.closed?
681
753
  @heartbeat_before = Time.now.to_f
682
- @connection.write({ op: 1, d: @last_s }.to_json)
683
- @connection.flush
684
- @log.debug "Sent opcode 1."
685
- @log.debug "Waiting for heartbeat."
754
+ connection.write({ op: 1, d: @last_s }.to_json)
755
+ connection.flush
756
+ logger.debug "Sent opcode 1."
757
+ logger.debug "Waiting for heartbeat."
686
758
  end
687
759
  sleep(interval / 1000.0 - 1)
688
760
  end
@@ -690,29 +762,30 @@ module Discorb
690
762
  end
691
763
 
692
764
  def handle_event(event_name, data)
693
- return @log.debug "Client isn't ready; event #{event_name} wasn't handled" if @wait_until_ready && !@ready && !%w[READY GUILD_CREATE].include?(event_name)
765
+ return logger.debug "Client isn't ready; event #{event_name} wasn't handled" if @wait_until_ready && !@ready && !%w[READY GUILD_CREATE].include?(event_name)
694
766
 
695
767
  dispatch(:event_receive, event_name, data)
696
- @log.debug "Handling event #{event_name}"
768
+ logger.debug "Handling event #{event_name}"
697
769
  case event_name
698
770
  when "READY"
699
771
  @api_version = data[:v]
700
- @session_id = data[:session_id]
772
+ self.session_id = data[:session_id]
701
773
  @user = ClientUser.new(self, data[:user])
702
774
  @uncached_guilds = data[:guilds].map { |g| g[:id] }
703
775
  ready if (@uncached_guilds == []) || !@intents.guilds
704
776
  dispatch(:ready)
777
+
705
778
  @tasks << handle_heartbeat
706
779
  when "GUILD_CREATE"
707
780
  if @uncached_guilds.include?(data[:id])
708
781
  Guild.new(self, data, true)
709
782
  @uncached_guilds.delete(data[:id])
710
783
  if @uncached_guilds == []
711
- @log.debug "All guilds cached"
784
+ logger.debug "All guilds cached"
712
785
  ready
713
786
  end
714
787
  elsif @guilds.has?(data[:id])
715
- @guilds[data[:id]].send(:_set_data, data)
788
+ @guilds[data[:id]].send(:_set_data, data, true)
716
789
  dispatch(:guild_available, guild)
717
790
  else
718
791
  guild = Guild.new(self, data, true)
@@ -729,51 +802,51 @@ module Discorb
729
802
  current.send(:_set_data, data, false)
730
803
  dispatch(:guild_update, before, current)
731
804
  else
732
- @log.warn "Unknown guild id #{data[:id]}, ignoring"
805
+ logger.warn "Unknown guild id #{data[:id]}, ignoring"
733
806
  end
734
807
  when "GUILD_DELETE"
735
- return @log.warn "Unknown guild id #{data[:id]}, ignoring" unless (guild = @guilds.delete(data[:id]))
808
+ return logger.warn "Unknown guild id #{data[:id]}, ignoring" unless (guild = @guilds.delete(data[:id]))
736
809
 
737
810
  dispatch(:guild_delete, guild)
738
- if guild.has?(:unavailable)
811
+ if data[:unavailable]
739
812
  dispatch(:guild_destroy, guild)
740
813
  else
741
814
  dispatch(:guild_leave, guild)
742
815
  end
743
816
  when "GUILD_ROLE_CREATE"
744
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
817
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
745
818
 
746
819
  nr = Role.new(@client, guild, data[:role])
747
820
  guild.roles[data[:role][:id]] = nr
748
821
  dispatch(:role_create, nr)
749
822
  when "GUILD_ROLE_UPDATE"
750
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
751
- return @log.warn "Unknown role id #{data[:role][:id]}, ignoring" unless guild.roles.has?(data[:role][:id])
823
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
824
+ return logger.warn "Unknown role id #{data[:role][:id]}, ignoring" unless guild.roles.has?(data[:role][:id])
752
825
 
753
826
  current = guild.roles[data[:role][:id]]
754
827
  before = Role.new(@client, guild, current.instance_variable_get(:@data).update({ no_cache: true }))
755
828
  current.send(:_set_data, data[:role])
756
829
  dispatch(:role_update, before, current)
757
830
  when "GUILD_ROLE_DELETE"
758
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
759
- return @log.warn "Unknown role id #{data[:role_id]}, ignoring" unless (role = guild.roles.delete(data[:role_id]))
831
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
832
+ return logger.warn "Unknown role id #{data[:role_id]}, ignoring" unless (role = guild.roles.delete(data[:role_id]))
760
833
 
761
834
  dispatch(:role_delete, role)
762
835
  when "CHANNEL_CREATE"
763
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
836
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
764
837
 
765
838
  nc = Channel.make_channel(self, data)
766
839
  guild.channels[data[:id]] = nc
767
840
 
768
841
  dispatch(:channel_create, nc)
769
842
  when "CHANNEL_UPDATE"
770
- return @log.warn "Unknown channel id #{data[:id]}, ignoring" unless (current = @channels[data[:id]])
843
+ return logger.warn "Unknown channel id #{data[:id]}, ignoring" unless (current = @channels[data[:id]])
771
844
 
772
845
  before = Channel.make_channel(self, current.instance_variable_get(:@data), no_cache: true)
773
846
  current.send(:_set_data, data)
774
847
  dispatch(:channel_update, before, current)
775
848
  when "CHANNEL_DELETE"
776
- return @log.warn "Unknown channel id #{data[:id]}, ignoring" unless (channel = @channels.delete(data[:id]))
849
+ return logger.warn "Unknown channel id #{data[:id]}, ignoring" unless (channel = @channels.delete(data[:id]))
777
850
 
778
851
  @guilds[data[:guild_id]]&.channels&.delete(data[:id])
779
852
  dispatch(:channel_delete, channel)
@@ -789,13 +862,13 @@ module Discorb
789
862
  dispatch(:thread_new, thread)
790
863
  end
791
864
  when "THREAD_UPDATE"
792
- return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
865
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
793
866
 
794
867
  before = Channel.make_channel(self, thread.instance_variable_get(:@data), no_cache: true)
795
868
  thread.send(:_set_data, data)
796
869
  dispatch(:thread_update, before, thread)
797
870
  when "THREAD_DELETE"
798
- return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels.delete(data[:id]))
871
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels.delete(data[:id]))
799
872
 
800
873
  @guilds[data[:guild_id]]&.channels&.delete(data[:id])
801
874
  dispatch(:thread_delete, thread)
@@ -805,11 +878,11 @@ module Discorb
805
878
  @channels[thread.id] = thread
806
879
  end
807
880
  when "THREAD_MEMBER_UPDATE"
808
- return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
881
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
809
882
 
810
883
  if (member = thread.members[data[:id]])
811
884
  old = ThreadChannel::Member.new(self, member.instance_variable_get(:@data))
812
- member._set_data(data)
885
+ member.send(:_set_data, data)
813
886
  else
814
887
  old = nil
815
888
  member = ThreadChannel::Member.new(self, data)
@@ -817,7 +890,7 @@ module Discorb
817
890
  end
818
891
  dispatch(:thread_member_update, thread, old, member)
819
892
  when "THREAD_MEMBERS_UPDATE"
820
- return @log.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
893
+ return logger.warn "Unknown thread id #{data[:id]}, ignoring" unless (thread = @channels[data[:id]])
821
894
 
822
895
  thread.instance_variable_set(:@member_count, data[:member_count])
823
896
  members = []
@@ -835,37 +908,37 @@ module Discorb
835
908
  instance = StageInstance.new(self, data)
836
909
  dispatch(:stage_instance_create, instance)
837
910
  when "STAGE_INSTANCE_UPDATE"
838
- return @log.warn "Unknown channel id #{data[:channel_id]} , ignoring" unless (channel = @channels[data[:channel_id]])
839
- return @log.warn "Unknown stage instance id #{data[:id]}, ignoring" unless (instance = channel.stage_instances[data[:id]])
911
+ return logger.warn "Unknown channel id #{data[:channel_id]} , ignoring" unless (channel = @channels[data[:channel_id]])
912
+ return logger.warn "Unknown stage instance id #{data[:id]}, ignoring" unless (instance = channel.stage_instances[data[:id]])
840
913
 
841
914
  old = StageInstance.new(self, instance.instance_variable_get(:@data), no_cache: true)
842
915
  current.send(:_set_data, data)
843
916
  dispatch(:stage_instance_update, old, current)
844
917
  when "STAGE_INSTANCE_DELETE"
845
- return @log.warn "Unknown channel id #{data[:channel_id]} , ignoring" unless (channel = @channels[data[:channel_id]])
846
- return @log.warn "Unknown stage instance id #{data[:id]}, ignoring" unless (instance = channel.stage_instances.delete(data[:id]))
918
+ return logger.warn "Unknown channel id #{data[:channel_id]} , ignoring" unless (channel = @channels[data[:channel_id]])
919
+ return logger.warn "Unknown stage instance id #{data[:id]}, ignoring" unless (instance = channel.stage_instances.delete(data[:id]))
847
920
 
848
921
  dispatch(:stage_instance_delete, instance)
849
922
  when "GUILD_MEMBER_ADD"
850
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
923
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
851
924
 
852
925
  nm = Member.new(self, data[:guild_id], data[:user].update({ no_cache: true }), data)
853
926
  guild.members[nm.id] = nm
854
927
  dispatch(:member_add, nm)
855
928
  when "GUILD_MEMBER_UPDATE"
856
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
857
- return @log.warn "Unknown member id #{data[:user][:id]}, ignoring" unless (nm = guild.members[data[:user][:id]])
929
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
930
+ return logger.warn "Unknown member id #{data[:user][:id]}, ignoring" unless (nm = guild.members[data[:user][:id]])
858
931
 
859
932
  old = Member.new(self, data[:guild_id], data[:user], data.update({ no_cache: true }))
860
933
  nm.send(:_set_data, data[:user], data)
861
934
  dispatch(:member_update, old, nm)
862
935
  when "GUILD_MEMBER_REMOVE"
863
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
864
- return @log.warn "Unknown member id #{data[:user][:id]}, ignoring" unless (member = guild.members.delete(data[:user][:id]))
936
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
937
+ return logger.warn "Unknown member id #{data[:user][:id]}, ignoring" unless (member = guild.members.delete(data[:user][:id]))
865
938
 
866
939
  dispatch(:member_remove, member)
867
940
  when "GUILD_BAN_ADD"
868
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
941
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
869
942
 
870
943
  user = if @users.has? data[:user][:id]
871
944
  @users[data[:user][:id]]
@@ -875,7 +948,7 @@ module Discorb
875
948
 
876
949
  dispatch(:guild_ban_add, guild, user)
877
950
  when "GUILD_BAN_REMOVE"
878
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
951
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
879
952
 
880
953
  user = if @users.has? data[:user][:id]
881
954
  @users[data[:user][:id]]
@@ -885,7 +958,7 @@ module Discorb
885
958
 
886
959
  dispatch(:guild_ban_remove, guild, user)
887
960
  when "GUILD_EMOJIS_UPDATE"
888
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
961
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
889
962
 
890
963
  before_emojis = guild.emojis.values.map(&:id).to_set
891
964
  data[:emojis].each do |emoji|
@@ -896,17 +969,16 @@ module Discorb
896
969
  guild.emojis.delete(emoji)
897
970
  end
898
971
  when "GUILD_INTEGRATIONS_UPDATE"
899
- # dispatch(:guild_integrations_update, GuildIntegrationsUpdateEvent.new(self, data))
900
- # Currently not implemented
972
+ dispatch(:guild_integrations_update, @guilds[data[:guild_id]])
901
973
  when "INTEGRATION_CREATE"
902
974
  dispatch(:integration_create, Integration.new(self, data, data[:guild_id]))
903
975
  when "INTEGRATION_UPDATE"
904
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
976
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
905
977
 
906
978
  integration = Integration.new(self, data, data[:guild_id])
907
979
  dispatch(:integration_update, integration)
908
980
  when "INTEGRATION_DELETE"
909
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
981
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
910
982
 
911
983
  dispatch(:integration_delete, IntegrationDeleteEvent.new(self, data))
912
984
  when "WEBHOOKS_UPDATE"
@@ -916,7 +988,7 @@ module Discorb
916
988
  when "INVITE_DELETE"
917
989
  dispatch(:invite_delete, InviteDeleteEvent.new(self, data))
918
990
  when "VOICE_STATE_UPDATE"
919
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
991
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
920
992
 
921
993
  current = guild.voice_states[data[:user_id]]
922
994
  if current.nil?
@@ -1013,7 +1085,7 @@ module Discorb
1013
1085
  end
1014
1086
  end
1015
1087
  when "PRESENCE_UPDATE"
1016
- return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
1088
+ return logger.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
1017
1089
 
1018
1090
  guild.presences[data[:user][:id]] = Presence.new(self, data)
1019
1091
  when "MESSAGE_UPDATE"
@@ -1089,7 +1161,7 @@ module Discorb
1089
1161
  (target_reaction = target_message.reactions.find { |r| data[:emoji][:id].nil? ? r.name == data[:emoji][:name] : r.id == data[:emoji][:id] })
1090
1162
  target_message.reactions.delete(target_reaction)
1091
1163
  end
1092
- dispatch(:reaction_remove_emoji, ReactionRemoveEmojiEvent.new(data))
1164
+ dispatch(:reaction_remove_emoji, ReactionRemoveEmojiEvent.new(self, data))
1093
1165
  when "TYPING_START"
1094
1166
  dispatch(:typing_start, TypingStartEvent.new(self, data))
1095
1167
  when "INTERACTION_CREATE"
@@ -1098,17 +1170,21 @@ module Discorb
1098
1170
 
1099
1171
  dispatch(interaction.class.event_name, interaction)
1100
1172
  when "RESUMED"
1101
- @log.info("Successfully resumed connection")
1173
+ logger.info("Successfully resumed connection")
1102
1174
  @tasks << handle_heartbeat
1103
- dispatch(:resumed)
1175
+ if shard
1176
+ dispatch(:shard_resumed, shard)
1177
+ else
1178
+ dispatch(:resumed)
1179
+ end
1104
1180
  when "GUILD_SCHEDULED_EVENT_CREATE"
1105
- @log.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1181
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1106
1182
  event = ScheduledEvent.new(self, data)
1107
1183
  guild.scheduled_events[data[:id]] = event
1108
1184
  dispatch(:scheduled_event_create, event)
1109
1185
  when "GUILD_SCHEDULED_EVENT_UPDATE"
1110
- @log.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1111
- @log.warn("Unknown scheduled event id #{data[:id]}, ignoring") unless (event = guild.scheduled_events[data[:id]])
1186
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1187
+ logger.warn("Unknown scheduled event id #{data[:id]}, ignoring") unless (event = guild.scheduled_events[data[:id]])
1112
1188
  old = event.dup
1113
1189
  event.send(:_set_data, data)
1114
1190
  dispatch(:scheduled_event_update, old, event)
@@ -1123,22 +1199,22 @@ module Discorb
1123
1199
  end
1124
1200
  end
1125
1201
  when "GUILD_SCHEDULED_EVENT_DELETE"
1126
- @log.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1127
- @log.warn("Unknown scheduled event id #{data[:id]}, ignoring") unless (event = guild.scheduled_events[data[:id]])
1202
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1203
+ logger.warn("Unknown scheduled event id #{data[:id]}, ignoring") unless (event = guild.scheduled_events[data[:id]])
1128
1204
  guild.scheduled_events.remove(data[:id])
1129
1205
  dispatch(:scheduled_event_delete, event)
1130
1206
  dispatch(:scheduled_event_cancel, event)
1131
1207
  when "GUILD_SCHEDULED_EVENT_USER_ADD"
1132
- @log.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1208
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1133
1209
  dispatch(:scheduled_event_user_add, ScheduledEventUserEvent.new(self, data))
1134
1210
  when "GUILD_SCHEDULED_EVENT_USER_REMOVE"
1135
- @log.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1211
+ logger.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
1136
1212
  dispatch(:scheduled_event_user_remove, ScheduledEventUserEvent.new(self, data))
1137
1213
  else
1138
1214
  if respond_to?("event_" + event_name.downcase)
1139
1215
  __send__("event_" + event_name.downcase, data)
1140
1216
  else
1141
- @log.debug "Unhandled event: #{event_name}\n#{data.inspect}"
1217
+ logger.debug "Unhandled event: #{event_name}\n#{data.inspect}"
1142
1218
  end
1143
1219
  end
1144
1220
  end
@@ -1146,7 +1222,7 @@ module Discorb
1146
1222
  def ready
1147
1223
  Async do
1148
1224
  if @fetch_member
1149
- @log.debug "Fetching members"
1225
+ logger.debug "Fetching members"
1150
1226
  barrier = Async::Barrier.new
1151
1227
 
1152
1228
  @guilds.each do |guild|
@@ -1157,8 +1233,25 @@ module Discorb
1157
1233
  barrier.wait
1158
1234
  end
1159
1235
  @ready = true
1160
- dispatch(:standby)
1161
- @log.info("Client is ready!")
1236
+
1237
+ if self.shard
1238
+ logger.info("Shard #{shard_id} is ready!")
1239
+ self.shard&.tap do |shard|
1240
+ if shard.next_shard
1241
+ dispatch(:shard_standby, shard)
1242
+ shard.next_shard.tap do |next_shard|
1243
+ logger.debug("Starting shard #{next_shard.id}")
1244
+ next_shard.start
1245
+ end
1246
+ else
1247
+ logger.info("All shards are ready!")
1248
+ dispatch(:standby)
1249
+ end
1250
+ end
1251
+ else
1252
+ logger.info("Client is ready!")
1253
+ dispatch(:standby)
1254
+ end
1162
1255
  end
1163
1256
  end
1164
1257
  end
@@ -1173,6 +1266,10 @@ module Discorb
1173
1266
  @closed = false
1174
1267
  end
1175
1268
 
1269
+ def inspect
1270
+ "<#{self.class.name} #{io.fileno}>"
1271
+ end
1272
+
1176
1273
  def closed?
1177
1274
  @closed
1178
1275
  end
@@ -1185,10 +1282,14 @@ module Discorb
1185
1282
  end
1186
1283
 
1187
1284
  def force_close
1188
- @framer.instance_variable_get(:@stream).instance_variable_get(:@io).instance_variable_get(:@io).instance_variable_get(:@io).close
1285
+ io.close
1189
1286
  @closed = true
1190
1287
  end
1191
1288
 
1289
+ def io
1290
+ @framer.instance_variable_get(:@stream).instance_variable_get(:@io).instance_variable_get(:@io).instance_variable_get(:@io)
1291
+ end
1292
+
1192
1293
  def parse(buffer)
1193
1294
  # noop
1194
1295
  buffer.to_s