discorb 0.13.1 → 0.14.0

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