discorb 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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