discorb 0.13.1 → 0.14.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/build_version.yml +3 -3
  4. data/.github/workflows/codeql-analysis.yml +70 -0
  5. data/.github/workflows/lint-push.yml +18 -0
  6. data/.github/workflows/lint.yml +16 -0
  7. data/.rubocop.yml +70 -0
  8. data/CODE_OF_CONDUCT.md +128 -0
  9. data/Changelog.md +33 -0
  10. data/Gemfile +7 -3
  11. data/README.md +1 -1
  12. data/Rakefile +35 -35
  13. data/discorb.gemspec +13 -1
  14. data/examples/commands/bookmarker.rb +2 -1
  15. data/examples/commands/hello.rb +1 -0
  16. data/examples/commands/inspect.rb +3 -2
  17. data/examples/components/authorization_button.rb +2 -1
  18. data/examples/components/select_menu.rb +2 -1
  19. data/examples/extension/main.rb +1 -0
  20. data/examples/extension/message_expander.rb +1 -0
  21. data/examples/simple/eval.rb +3 -2
  22. data/examples/simple/ping_pong.rb +1 -0
  23. data/examples/simple/rolepanel.rb +1 -0
  24. data/examples/simple/wait_for_message.rb +4 -3
  25. data/exe/discorb +8 -7
  26. data/lib/discorb/allowed_mentions.rb +64 -0
  27. data/lib/discorb/app_command/command.rb +274 -0
  28. data/lib/discorb/app_command/handler.rb +168 -0
  29. data/lib/discorb/app_command.rb +2 -404
  30. data/lib/discorb/asset.rb +3 -1
  31. data/lib/discorb/audit_logs.rb +3 -3
  32. data/lib/discorb/channel.rb +89 -53
  33. data/lib/discorb/client.rb +36 -33
  34. data/lib/discorb/common.rb +28 -21
  35. data/lib/discorb/components/button.rb +106 -0
  36. data/lib/discorb/components/select_menu.rb +157 -0
  37. data/lib/discorb/components/text_input.rb +96 -0
  38. data/lib/discorb/components.rb +11 -276
  39. data/lib/discorb/dictionary.rb +13 -2
  40. data/lib/discorb/embed.rb +2 -2
  41. data/lib/discorb/emoji.rb +21 -5
  42. data/lib/discorb/emoji_table.rb +1 -1
  43. data/lib/discorb/error.rb +4 -6
  44. data/lib/discorb/event.rb +13 -11
  45. data/lib/discorb/exe/about.rb +1 -0
  46. data/lib/discorb/exe/irb.rb +4 -3
  47. data/lib/discorb/exe/new.rb +6 -7
  48. data/lib/discorb/exe/run.rb +2 -1
  49. data/lib/discorb/exe/setup.rb +8 -5
  50. data/lib/discorb/exe/show.rb +1 -0
  51. data/lib/discorb/extend.rb +19 -14
  52. data/lib/discorb/extension.rb +5 -1
  53. data/lib/discorb/gateway.rb +75 -27
  54. data/lib/discorb/guild.rb +58 -80
  55. data/lib/discorb/guild_template.rb +5 -5
  56. data/lib/discorb/http.rb +34 -169
  57. data/lib/discorb/integration.rb +32 -3
  58. data/lib/discorb/intents.rb +1 -1
  59. data/lib/discorb/interaction/autocomplete.rb +5 -4
  60. data/lib/discorb/interaction/command.rb +34 -9
  61. data/lib/discorb/interaction/components.rb +5 -2
  62. data/lib/discorb/interaction/modal.rb +33 -0
  63. data/lib/discorb/interaction/response.rb +41 -12
  64. data/lib/discorb/interaction/root.rb +1 -0
  65. data/lib/discorb/interaction.rb +2 -1
  66. data/lib/discorb/invite.rb +1 -1
  67. data/lib/discorb/log.rb +4 -3
  68. data/lib/discorb/member.rb +4 -6
  69. data/lib/discorb/message.rb +31 -282
  70. data/lib/discorb/message_meta.rb +205 -0
  71. data/lib/discorb/modules.rb +11 -11
  72. data/lib/discorb/permission.rb +2 -2
  73. data/lib/discorb/presence.rb +6 -3
  74. data/lib/discorb/rate_limit.rb +15 -21
  75. data/lib/discorb/role.rb +3 -3
  76. data/lib/discorb/sticker.rb +2 -2
  77. data/lib/discorb/user.rb +3 -3
  78. data/lib/discorb/utils/colored_puts.rb +1 -0
  79. data/lib/discorb/voice_state.rb +7 -2
  80. data/lib/discorb/webhook.rb +8 -5
  81. data/lib/discorb.rb +1 -0
  82. data/template-replace/scripts/arrow.rb +1 -0
  83. data/template-replace/scripts/favicon.rb +1 -0
  84. data/template-replace/scripts/index.rb +2 -1
  85. data/template-replace/scripts/locale_ja.rb +5 -4
  86. data/template-replace/scripts/sidebar.rb +1 -0
  87. data/template-replace/scripts/version.rb +7 -10
  88. data/template-replace/scripts/yard_replace.rb +5 -4
  89. metadata +29 -4
data/lib/discorb/embed.rb CHANGED
@@ -79,11 +79,11 @@ module Discorb
79
79
  end
80
80
 
81
81
  def image=(value)
82
- @image = (value.is_a?(String)) ? Image.new(value) : value
82
+ @image = value.is_a?(String) ? Image.new(value) : value
83
83
  end
84
84
 
85
85
  def thumbnail=(value)
86
- @thumbnail = (value.is_a?(String)) ? Thumbnail.new(value) : value
86
+ @thumbnail = value.is_a?(String) ? Thumbnail.new(value) : value
87
87
  end
88
88
 
89
89
  def inspect
data/lib/discorb/emoji.rb CHANGED
@@ -96,7 +96,7 @@ module Discorb
96
96
  payload = {}
97
97
  payload[:name] = name if name != Discorb::Unset
98
98
  payload[:roles] = roles.map { |r| Discorb::Utils.try(r, :id) } if roles != Discorb::Unset
99
- @client.http.patch("/guilds/#{@guild.id}/emojis/#{@id}", payload, audit_log_reason: reason)
99
+ @client.http.request(Route.new("/guilds/#{@guild.id}/emojis/#{@id}", "//guilds/:guild_id/emojis/:emoji_id", :patch), payload, audit_log_reason: reason)
100
100
  self
101
101
  end
102
102
  end
@@ -113,7 +113,7 @@ module Discorb
113
113
  #
114
114
  def delete!(reason: nil)
115
115
  Async do
116
- @client.http.delete("/guilds/#{@guild.id}/emojis/#{@id}", audit_log_reason: reason).wait
116
+ @client.http.request(Route.new("/guilds/#{@guild.id}/emojis/#{@id}", "//guilds/:guild_id/emojis/:emoji_id", :delete), audit_log_reason: reason).wait
117
117
  @available = false
118
118
  self
119
119
  end
@@ -121,6 +121,15 @@ module Discorb
121
121
 
122
122
  alias destroy! delete!
123
123
 
124
+ # @private
125
+ def to_hash
126
+ {
127
+ name: @name,
128
+ id: @id,
129
+ animated: @animated,
130
+ }
131
+ end
132
+
124
133
  private
125
134
 
126
135
  def _set_data(data)
@@ -202,9 +211,7 @@ module Discorb
202
211
  else
203
212
  raise ArgumentError, "No such emoji: #{name}"
204
213
  end
205
- if tone > 0
206
- @value += EmojiTable::SKIN_TONES[tone]
207
- end
214
+ @value += EmojiTable::SKIN_TONES[tone] if tone.positive?
208
215
  end
209
216
 
210
217
  # @return [String] The unicode string of the emoji.
@@ -225,6 +232,15 @@ module Discorb
225
232
  "#<#{self.class} :#{@name}:>"
226
233
  end
227
234
 
235
+ # @private
236
+ def to_hash
237
+ {
238
+ name: @value,
239
+ id: nil,
240
+ animated: false,
241
+ }
242
+ end
243
+
228
244
  class << self
229
245
  alias [] new
230
246
  end
@@ -3885,7 +3885,7 @@ module Discorb
3885
3885
  "\xf0\x9f\x8f\xbc",
3886
3886
  "\xf0\x9f\x8f\xbd",
3887
3887
  "\xf0\x9f\x8f\xbe",
3888
- "\xf0\x9f\x8f\xbf",
3888
+ "\xf0\x9f\x8f\xbf"
3889
3889
  ].freeze
3890
3890
  end
3891
3891
  end
data/lib/discorb/error.rb CHANGED
@@ -11,9 +11,7 @@ module Discorb
11
11
  def enumerate_errors(hash)
12
12
  res = {}
13
13
  _recr_items([], hash, res)
14
- if res == { "" => nil }
15
- res = {}
16
- end
14
+ res = {} if res == { "" => nil }
17
15
  res
18
16
  end
19
17
 
@@ -64,7 +62,7 @@ module Discorb
64
62
  [
65
63
  data[:message] + " (#{@code})", enumerate_errors(data[:errors])
66
64
  .map { |ek, ev| "#{ek}=>#{ev}" }
67
- .join("\n"),
65
+ .join("\n")
68
66
  ].join("\n")
69
67
  )
70
68
  end
@@ -92,14 +90,14 @@ module Discorb
92
90
  # Represents a error because of a cloudflare ban.
93
91
  #
94
92
  class CloudFlareBanError < HTTPError
95
- def initialize(resp, client)
93
+ def initialize(_resp, client)
96
94
  @client = client
97
95
  @client.close!
98
96
  message = <<~MESSAGE
99
97
  The client is banned from CloudFlare.
100
98
  Hint: Try to decrease the number of requests per second, e.g. Use sleep in between requests.
101
99
  MESSAGE
102
- $stderr.puts message
100
+ warn message
103
101
  DiscorbError.instance_method(:initialize).bind(self).call(message)
104
102
  end
105
103
  end
data/lib/discorb/event.rb CHANGED
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
3
+ #
4
+ # Represents an event in guild.
5
+ #
2
6
  class ScheduledEvent < DiscordModel
3
7
  @privacy_level = {
4
8
  2 => :guild_only,
@@ -120,7 +124,7 @@ module Discorb
120
124
  status: Discorb::Unset
121
125
  )
122
126
  Async do
123
- payload = case (type == Discorb::Unset) ? @entity_type : type
127
+ payload = case type == Discorb::Unset ? @entity_type : type
124
128
  when :stage_instance
125
129
  raise ArgumentError, "channel must be provided for stage_instance events" unless channel
126
130
  {
@@ -164,7 +168,7 @@ module Discorb
164
168
  else
165
169
  raise ArgumentError, "Invalid scheduled event type: #{type}"
166
170
  end
167
- @client.http.patch("/guilds/#{@guild_id}/scheduled-events/#{@id}", payload).wait
171
+ @client.http.request(Route.new("/guilds/#{@guild_id}/scheduled-events/#{@id}", "//guilds/:guild_id/scheduled-events/:scheduled_event_id", :patch), payload).wait
168
172
  end
169
173
  end
170
174
 
@@ -201,7 +205,7 @@ module Discorb
201
205
  #
202
206
  def delete!
203
207
  Async do
204
- @client.http.delete("/guilds/#{@guild_id}/scheduled-events/#{@id}").wait
208
+ @client.http.request(Route.new("/guilds/#{@guild_id}/scheduled-events/#{@id}", "//guilds/:guild_id/scheduled-events/:scheduled_event_id", :delete)).wait
205
209
  end
206
210
  end
207
211
 
@@ -226,11 +230,9 @@ module Discorb
226
230
  if limit.nil?
227
231
  after = 0
228
232
  res = []
229
- while true
230
- _resp, users = @client.http.get("/guilds/#{@guild_id}/scheduled-events/#{@id}/users?limit=100&after=#{after}&with_member=true").wait
231
- if users.empty?
232
- break
233
- end
233
+ loop do
234
+ _resp, users = @client.http.request(Route.new("/guilds/#{@guild_id}/scheduled-events/#{@id}/users?limit=100&after=#{after}&with_member=true", "//guilds/:guild_id/scheduled-events/:scheduled_event_id/users", :get)).wait
235
+ break if users.empty?
234
236
  res += users.map { |u| Member.new(@client, @guild_id, u[:user], u[:member]) }
235
237
  after = users.last[:user][:id]
236
238
  end
@@ -238,11 +240,11 @@ module Discorb
238
240
  else
239
241
  params = {
240
242
  limit: limit,
241
- before: Discorb::Utils.try(after, :id),
242
- after: Discorb::Utils.try(around, :id),
243
+ before: Discorb::Utils.try(before, :id),
244
+ after: Discorb::Utils.try(after, :id),
243
245
  with_member: with_member,
244
246
  }.filter { |_k, v| !v.nil? }.to_h
245
- _resp, messages = @client.http.get("/channels/#{channel_id.wait}/messages?#{URI.encode_www_form(params)}").wait
247
+ _resp, messages = @client.http.request(Route.new("/channels/#{channel_id.wait}/messages?#{URI.encode_www_form(params)}", "//channels/:channel_id/messages", :get)).wait
246
248
  messages.map { |m| Message.new(@client, m.merge({ guild_id: @guild_id.to_s })) }
247
249
  end
248
250
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # description: Show information of discorb.
2
3
 
3
4
  puts " disco\e[31mrb\e[m - A new Discord API wrapper in Ruby. \n\n"
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # description: Connect to discord and start IRB.
4
5
 
@@ -31,13 +32,13 @@ client.on :standby do
31
32
  end
32
33
 
33
34
  def dirb_help
34
- puts <<~EOS
35
+ puts <<~MESSAGE
35
36
  \e[96mDiscord-IRB\e[m
36
37
  This is a debug client for Discord.
37
38
  \e[90mmessage\e[m to get latest message.
38
39
 
39
40
  \e[36mhttps://discorb-lib.github.io/#{Discorb::VERSION}/file.irb.html\e[m for more information.
40
- EOS
41
+ MESSAGE
41
42
  end
42
43
 
43
44
  puts <<~FIRST_MESSAGE
@@ -56,7 +57,7 @@ end
56
57
 
57
58
  token = ENV["DISCORD_BOT_TOKEN"] || ENV["DISCORD_TOKEN"]
58
59
  if token.nil?
59
- if File.exists?(token_file)
60
+ if File.exist?(token_file)
60
61
  token = File.read(token_file)
61
62
  else
62
63
  print "\e[90mToken?\e[m : "
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # description: Make files for the discorb project.
2
3
 
3
4
  require "optparse"
@@ -131,7 +132,7 @@ FILES = {
131
132
  https://rubygems.org/gems/license-cli may be useful.
132
133
 
133
134
  MARKDOWN
134
- }
135
+ }.freeze
135
136
 
136
137
  # @private
137
138
  def create_file(name)
@@ -162,8 +163,8 @@ def git_init
162
163
  system "git init"
163
164
  system "git add ."
164
165
  system "git commit -m \"Initial commit\""
165
- sputs "Initialized repository, use " +
166
- "\e[32mgit commit --amend -m '...'\e[92m" +
166
+ sputs "Initialized repository, use " \
167
+ "\e[32mgit commit --amend -m '...'\e[92m" \
167
168
  " to change commit message of initial commit.\n"
168
169
  end
169
170
 
@@ -225,13 +226,11 @@ if (dir = ARGV[0])
225
226
  if Dir.exist?($path)
226
227
  if Dir.empty?($path)
227
228
  iputs "Found \e[30m#{dir}\e[90m and empty, using this directory."
228
- else
229
- if $values[:force]
230
- iputs "Found \e[30m#{dir}\e[90m and not empty, but force is on, using this directory."
229
+ elsif $values[:force]
230
+ iputs "Found \e[30m#{dir}\e[90m and not empty, but force is on, using this directory."
231
231
  else
232
232
  eputs "Directory \e[31m#{dir}\e[91m already exists and not empty. Use \e[31m-f\e[91m to force."
233
233
  exit
234
- end
235
234
  end
236
235
  else
237
236
  Dir.mkdir($path)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # description: Run a client.
2
3
  require "optparse"
3
4
  require "json"
@@ -6,7 +7,7 @@ require "io/console"
6
7
 
7
8
  ARGV.delete_at 0
8
9
  # @private
9
- LOG_LEVELS = %w[none debug info warn error fatal]
10
+ LOG_LEVELS = %w[none debug info warn error fatal].freeze
10
11
 
11
12
  opt = OptionParser.new <<~BANNER
12
13
  This command will run a client.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # description: Setup application commands.
2
3
  require "optparse"
3
4
  require "discorb/utils/colored_puts"
@@ -6,6 +7,7 @@ ARGV.delete_at 0
6
7
 
7
8
  options = {
8
9
  guilds: nil,
10
+ script: true,
9
11
  }
10
12
 
11
13
  opt = OptionParser.new <<~BANNER
@@ -16,20 +18,21 @@ opt = OptionParser.new <<~BANNER
16
18
  script The script to setup.
17
19
  BANNER
18
20
  opt.on("-g", "--guild ID", Array, "The guild ID to setup, use comma for setup commands in multiple guilds, or `global` for setup global commands.") { |v| options[:guilds] = v }
21
+ opt.on("-s", "--[no-]script", "Whether to run `:setup` event. This may be useful if setup script includes operation that shouldn't run twice. Default to true.") { |v| options[:script] = v }
19
22
  opt.parse!(ARGV)
20
23
 
21
24
  script = ARGV[0]
22
25
  script ||= "main.rb"
23
26
  ENV["DISCORB_CLI_FLAG"] = "setup"
24
27
 
25
- if options[:guilds] == ["global"]
26
- ENV["DISCORB_SETUP_GUILDS"] = "global"
28
+ ENV["DISCORB_SETUP_GUILDS"] = if options[:guilds] == ["global"]
29
+ "global"
27
30
  elsif options[:guilds]
28
- ENV["DISCORB_SETUP_GUILDS"] = options[:guilds].join(",")
29
- else
30
- ENV["DISCORB_SETUP_GUILDS"] = nil
31
+ options[:guilds].join(",")
31
32
  end
32
33
 
34
+ ENV["DISCORB_SETUP_SCRIPT"] = options[:script].to_s if options[:script]
35
+
33
36
  begin
34
37
  load script
35
38
  rescue LoadError
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # description: Show information of your environment.
2
3
 
3
4
  require "etc"
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # rubocop: disable Style/Documentation
2
3
 
3
4
  class Time
4
5
  #
@@ -18,22 +19,26 @@ class Time
18
19
  end
19
20
 
20
21
  # @private
21
- class Async::Node
22
- def description
23
- @object_name ||= "#{self.class}:0x#{object_id.to_s(16)}#{@transient ? " transient" : nil}"
22
+ module Async
23
+ class Node
24
+ def description
25
+ @object_name ||= "#{self.class}:0x#{object_id.to_s(16)}#{@transient ? " transient" : nil}"
24
26
 
25
- if @annotation
26
- "#{@object_name} #{@annotation}"
27
- elsif line = self.backtrace(0, 1)&.first
28
- "#{@object_name} #{line}"
29
- else
30
- @object_name
27
+ if @annotation
28
+ "#{@object_name} #{@annotation}"
29
+ elsif line = self.backtrace(0, 1)&.first
30
+ "#{@object_name} #{line}"
31
+ else
32
+ @object_name
33
+ end
31
34
  end
32
- end
33
35
 
34
- def to_s
35
- "\#<#{self.description}>"
36
- end
36
+ def to_s
37
+ "\#<#{self.description}>"
38
+ end
37
39
 
38
- alias inspect to_s
40
+ alias inspect to_s
41
+ end
39
42
  end
43
+
44
+ # rubocop: enable Style/Documentation
@@ -17,7 +17,7 @@ module Discorb
17
17
  ret = {}
18
18
  self.class.events.each do |event, handlers|
19
19
  ret[event] = handlers.map do |handler|
20
- Discorb::EventHandler.new(Proc.new { |*args, **kwargs| instance_exec(*args, **kwargs, &handler[2]) }, handler[0], handler[1])
20
+ Discorb::EventHandler.new(proc { |*args, **kwargs| instance_exec(*args, **kwargs, &handler[2]) }, handler[0], handler[1])
21
21
  end
22
22
  end
23
23
  @events = ret
@@ -27,6 +27,10 @@ module Discorb
27
27
  base.extend(ClassMethods)
28
28
  end
29
29
 
30
+ #
31
+ # @private
32
+ # Module for adding class methods to the extension class.
33
+ #
30
34
  module ClassMethods
31
35
  include Discorb::ApplicationCommand::Handler
32
36
  undef setup_commands
@@ -108,6 +108,35 @@ module Discorb
108
108
  end
109
109
  end
110
110
 
111
+ #
112
+ # Represents a `INTEGRATION_DELETE` event.
113
+ #
114
+ class IntegrationDeleteEvent < GatewayEvent
115
+ # @return [Discorb::Snowflake] The ID of the integration.
116
+ attr_reader :id
117
+ # @!attribute [r] guild
118
+ # @macro client_cache
119
+ # @return [Discorb::Guild] The guild of the integration.
120
+ # @!attribute [r] user
121
+ # @macro client_cache
122
+ # @return [Discorb::User] The user associated with the integration.
123
+
124
+ # @private
125
+ def initialize(_client, data)
126
+ @id = Snowflake.new(data[:id])
127
+ @guild_id = data[:guild_id]
128
+ @user_id = data[:application_id]
129
+ end
130
+
131
+ def guild
132
+ @client.guilds[@guild_id]
133
+ end
134
+
135
+ def user
136
+ @client.users[@user_id]
137
+ end
138
+ end
139
+
111
140
  #
112
141
  # Represents a `MESSAGE_REACTION_REMOVE_ALL` event.
113
142
  #
@@ -232,7 +261,6 @@ module Discorb
232
261
  #
233
262
  # Represents a `MESSAGE_UPDATE` event.
234
263
  #
235
-
236
264
  class MessageUpdateEvent < GatewayEvent
237
265
  # @return [Discorb::Message] The message before update.
238
266
  attr_reader :before
@@ -364,6 +392,9 @@ module Discorb
364
392
  end
365
393
  end
366
394
 
395
+ #
396
+ # Represents a `GUILD_INTEGRATIONS_UPDATE` event.
397
+ #
367
398
  class GuildIntegrationsUpdateEvent < GatewayEvent
368
399
  def initialize(client, data)
369
400
  @client = client
@@ -499,16 +530,20 @@ module Discorb
499
530
  module Handler
500
531
  private
501
532
 
502
- def connect_gateway(reconnect, no_close: false)
533
+ def connect_gateway(reconnect)
503
534
  if reconnect
504
535
  @log.info "Reconnecting to gateway..."
505
536
  else
506
537
  @log.info "Connecting to gateway..."
507
538
  end
508
539
  Async do
509
- @connection&.close unless no_close
540
+ if @connection && !@connection.closed?
541
+ Async do
542
+ @connection.close
543
+ end
544
+ end
510
545
  @http = HTTP.new(self)
511
- _, gateway_response = @http.get("/gateway").wait
546
+ _, gateway_response = @http.request(Route.new("/gateway", "//gateway", :get)).wait
512
547
  gateway_url = gateway_response[:url]
513
548
  endpoint = Async::HTTP::Endpoint.parse("#{gateway_url}?v=9&encoding=json&compress=zlib-stream",
514
549
  alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
@@ -533,9 +568,16 @@ module Discorb
533
568
  end
534
569
  end
535
570
  end
536
- rescue Async::Wrapper::Cancelled, OpenSSL::SSL::SSLError, Async::Wrapper::WaitError, EOFError, Errno::EPIPE => e
571
+ rescue Async::Wrapper::Cancelled,
572
+ OpenSSL::SSL::SSLError,
573
+ Async::Wrapper::WaitError,
574
+ EOFError,
575
+ Errno::EPIPE,
576
+ Errno::ECONNRESET,
577
+ IOError => e
537
578
  @log.error "Gateway connection closed accidentally: #{e.class}: #{e.message}"
538
- connect_gateway(true, no_close: true)
579
+ @connection.force_close
580
+ connect_gateway(true)
539
581
  else # should never happen
540
582
  connect_gateway(true)
541
583
  end
@@ -550,13 +592,13 @@ module Discorb
550
592
  when 4014
551
593
  raise ClientError.new("Disallowed intents were specified"), cause: nil
552
594
  when 4002, 4003, 4005, 4007
553
- raise ClientError.new(<<~EOS), cause: e
554
- Disconnected from gateway, probably due to library issues.
555
- #{e.message}
595
+ raise ClientError.new(<<~ERROR), cause: e
596
+ Disconnected from gateway, probably due to library issues.
597
+ #{e.message}
556
598
 
557
- Please report this to the library issue tracker.
558
- https://github.com/discorb-lib/discorb/issues
559
- EOS
599
+ Please report this to the library issue tracker.
600
+ https://github.com/discorb-lib/discorb/issues
601
+ ERROR
560
602
  when 1001
561
603
  @log.info "Gateway closed with code 1001, reconnecting."
562
604
  connect_gateway(true)
@@ -565,8 +607,9 @@ module Discorb
565
607
  @log.debug "#{e.message}"
566
608
  connect_gateway(false)
567
609
  end
568
- rescue => e
610
+ rescue StandardError => e
569
611
  @log.error "Discord WebSocket error: #{e.full_message}"
612
+ @connection.force_close
570
613
  connect_gateway(false)
571
614
  end
572
615
  end
@@ -579,7 +622,7 @@ module Discorb
579
622
  end
580
623
 
581
624
  def handle_gateway(payload, reconnect)
582
- Async do |task|
625
+ Async do |_task|
583
626
  data = payload[:d]
584
627
  @last_s = payload[:s] if payload[:s]
585
628
  @log.debug "Received message with opcode #{payload[:op]} from gateway:"
@@ -605,7 +648,7 @@ module Discorb
605
648
  send_gateway(2, **payload)
606
649
  end
607
650
  when 7
608
- @log.info "Received opcode 7, reconnecting"
651
+ @log.info "Received opcode 7, stopping tasks"
609
652
  @tasks.map(&:stop)
610
653
  when 9
611
654
  @log.warn "Received opcode 9, closed connection"
@@ -630,7 +673,7 @@ module Discorb
630
673
  end
631
674
 
632
675
  def handle_heartbeat
633
- Async do |task|
676
+ Async do |_task|
634
677
  interval = @heartbeat_interval
635
678
  sleep((interval / 1000.0 - 1) * rand)
636
679
  loop do
@@ -657,9 +700,7 @@ module Discorb
657
700
  @session_id = data[:session_id]
658
701
  @user = ClientUser.new(self, data[:user])
659
702
  @uncached_guilds = data[:guilds].map { |g| g[:id] }
660
- if @uncached_guilds == [] or !@intents.guilds
661
- ready
662
- end
703
+ ready if (@uncached_guilds == []) || !@intents.guilds
663
704
  dispatch(:ready)
664
705
  @tasks << handle_heartbeat
665
706
  when "GUILD_CREATE"
@@ -862,13 +903,12 @@ module Discorb
862
903
  when "INTEGRATION_UPDATE"
863
904
  return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
864
905
 
865
- before = Integration.new(self, data, data[:guild_id])
906
+ integration = Integration.new(self, data, data[:guild_id])
866
907
  dispatch(:integration_update, integration)
867
908
  when "INTEGRATION_DELETE"
868
909
  return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
869
- return @log.warn "Unknown integration id #{data[:id]}, ignoring" unless (integration = guild.integrations.delete(data[:id]))
870
910
 
871
- dispatch(:integration_delete, integration)
911
+ dispatch(:integration_delete, IntegrationDeleteEvent.new(self, data))
872
912
  when "WEBHOOKS_UPDATE"
873
913
  dispatch(:webhooks_update, WebhooksUpdateEvent.new(self, data))
874
914
  when "INVITE_CREATE"
@@ -884,6 +924,7 @@ module Discorb
884
924
  current = VoiceState.new(self, data)
885
925
  guild.voice_states[data[:user_id]] = current
886
926
  else
927
+ guild.voice_states.remove(data[:user_id]) if data[:channel_id].nil?
887
928
  old = VoiceState.new(self, current.instance_variable_get(:@data))
888
929
  current.send(:_set_data, data)
889
930
  end
@@ -1007,7 +1048,7 @@ module Discorb
1007
1048
  message.instance_variable_set(:@deleted, true)
1008
1049
  messages.push(message)
1009
1050
  else
1010
- messages.push(UnknownDeleteBulkMessage.new(self, id))
1051
+ messages.push(UnknownDeleteBulkMessage.new(self, id, data))
1011
1052
  end
1012
1053
  end
1013
1054
  dispatch(:message_delete_bulk, messages)
@@ -1053,7 +1094,7 @@ module Discorb
1053
1094
  dispatch(:typing_start, TypingStartEvent.new(self, data))
1054
1095
  when "INTERACTION_CREATE"
1055
1096
  interaction = Interaction.make_interaction(self, data)
1056
- dispatch(:integration_create, interaction)
1097
+ dispatch(:interaction_create, interaction)
1057
1098
 
1058
1099
  dispatch(interaction.class.event_name, interaction)
1059
1100
  when "RESUMED"
@@ -1071,15 +1112,15 @@ module Discorb
1071
1112
  old = event.dup
1072
1113
  event.send(:_set_data, data)
1073
1114
  dispatch(:scheduled_event_update, old, event)
1074
- if old.status != event.status
1115
+ if old.status == event.status
1116
+ dispatch(:scheduled_event_edit, old, event)
1117
+ else
1075
1118
  case event.status
1076
1119
  when :active
1077
1120
  dispatch(:scheduled_event_start, event)
1078
1121
  when :completed
1079
1122
  dispatch(:scheduled_event_end, event)
1080
1123
  end
1081
- else
1082
- dispatch(:scheduled_event_edit, old, event)
1083
1124
  end
1084
1125
  when "GUILD_SCHEDULED_EVENT_DELETE"
1085
1126
  @log.warn("Unknown guild id #{data[:guild_id]}, ignoring") unless (guild = @guilds[data[:guild_id]])
@@ -1139,6 +1180,13 @@ module Discorb
1139
1180
  def close
1140
1181
  super
1141
1182
  @closed = true
1183
+ rescue StandardError
1184
+ force_close
1185
+ end
1186
+
1187
+ def force_close
1188
+ @framer.instance_variable_get(:@stream).instance_variable_get(:@io).instance_variable_get(:@io).instance_variable_get(:@io).close
1189
+ @closed = true
1142
1190
  end
1143
1191
 
1144
1192
  def parse(buffer)