discorb 0.16.0 → 0.17.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/build_main.yml +2 -2
  4. data/.github/workflows/build_version.yml +1 -1
  5. data/.github/workflows/codeql-analysis.yml +1 -1
  6. data/.github/workflows/lint-push.yml +3 -5
  7. data/.github/workflows/lint.yml +1 -1
  8. data/.github/workflows/spec.yml +30 -0
  9. data/.lefthook/commit-msg/validator.rb +5 -0
  10. data/.rspec +2 -0
  11. data/.rspec_parallel +2 -0
  12. data/.rubocop.yml +43 -6
  13. data/Changelog.md +14 -1
  14. data/Gemfile +14 -8
  15. data/Rakefile +41 -26
  16. data/bin/console +3 -3
  17. data/docs/Examples.md +1 -1
  18. data/docs/application_command.md +138 -46
  19. data/docs/cli/irb.md +2 -2
  20. data/docs/cli/new.md +14 -9
  21. data/docs/cli/run.md +7 -11
  22. data/docs/cli.md +17 -10
  23. data/docs/events.md +209 -211
  24. data/docs/extension.md +1 -2
  25. data/docs/faq.md +0 -1
  26. data/docs/tutorial.md +12 -12
  27. data/docs/voice_events.md +106 -106
  28. data/examples/commands/message.rb +63 -0
  29. data/examples/commands/permission.rb +18 -0
  30. data/examples/commands/slash.rb +44 -0
  31. data/examples/commands/user.rb +51 -0
  32. data/examples/components/authorization_button.rb +2 -2
  33. data/examples/components/select_menu.rb +2 -2
  34. data/examples/extension/main.rb +1 -1
  35. data/examples/extension/message_expander.rb +5 -2
  36. data/examples/simple/eval.rb +2 -2
  37. data/examples/simple/ping_pong.rb +1 -1
  38. data/examples/simple/rolepanel.rb +1 -1
  39. data/examples/simple/shard.rb +1 -1
  40. data/examples/simple/wait_for_message.rb +1 -1
  41. data/exe/discorb +31 -16
  42. data/lefthook.yml +45 -0
  43. data/lib/discorb/allowed_mentions.rb +1 -0
  44. data/lib/discorb/app_command/command.rb +127 -65
  45. data/lib/discorb/app_command/common.rb +25 -0
  46. data/lib/discorb/app_command/handler.rb +115 -33
  47. data/lib/discorb/app_command.rb +2 -1
  48. data/lib/discorb/application.rb +1 -0
  49. data/lib/discorb/asset.rb +1 -2
  50. data/lib/discorb/attachment.rb +1 -1
  51. data/lib/discorb/audit_logs.rb +11 -8
  52. data/lib/discorb/channel/base.rb +108 -0
  53. data/lib/discorb/channel/category.rb +32 -0
  54. data/lib/discorb/channel/container.rb +44 -0
  55. data/lib/discorb/channel/dm.rb +28 -0
  56. data/lib/discorb/channel/guild.rb +245 -0
  57. data/lib/discorb/channel/stage.rb +140 -0
  58. data/lib/discorb/channel/text.rb +345 -0
  59. data/lib/discorb/channel/thread.rb +321 -0
  60. data/lib/discorb/channel/voice.rb +79 -0
  61. data/lib/discorb/channel.rb +2 -1165
  62. data/lib/discorb/client.rb +38 -26
  63. data/lib/discorb/common.rb +2 -1
  64. data/lib/discorb/components/button.rb +2 -1
  65. data/lib/discorb/components/select_menu.rb +4 -2
  66. data/lib/discorb/components/text_input.rb +12 -2
  67. data/lib/discorb/components.rb +1 -1
  68. data/lib/discorb/embed.rb +22 -7
  69. data/lib/discorb/emoji.rb +30 -3
  70. data/lib/discorb/emoji_table.rb +4969 -3
  71. data/lib/discorb/event.rb +29 -4
  72. data/lib/discorb/exe/about.rb +1 -0
  73. data/lib/discorb/exe/irb.rb +2 -4
  74. data/lib/discorb/exe/new.rb +90 -23
  75. data/lib/discorb/exe/run.rb +8 -22
  76. data/lib/discorb/exe/setup.rb +25 -12
  77. data/lib/discorb/exe/show.rb +4 -3
  78. data/lib/discorb/extend.rb +1 -0
  79. data/lib/discorb/extension.rb +6 -3
  80. data/lib/discorb/flag.rb +11 -0
  81. data/lib/discorb/gateway.rb +67 -19
  82. data/lib/discorb/guild.rb +188 -56
  83. data/lib/discorb/guild_template.rb +10 -4
  84. data/lib/discorb/http.rb +16 -9
  85. data/lib/discorb/integration.rb +4 -1
  86. data/lib/discorb/intents.rb +1 -1
  87. data/lib/discorb/interaction/autocomplete.rb +28 -16
  88. data/lib/discorb/interaction/command.rb +36 -12
  89. data/lib/discorb/interaction/components.rb +5 -2
  90. data/lib/discorb/interaction/modal.rb +0 -1
  91. data/lib/discorb/interaction/response.rb +61 -22
  92. data/lib/discorb/interaction/root.rb +13 -13
  93. data/lib/discorb/interaction.rb +1 -0
  94. data/lib/discorb/invite.rb +5 -2
  95. data/lib/discorb/member.rb +25 -5
  96. data/lib/discorb/message.rb +47 -14
  97. data/lib/discorb/message_meta.rb +1 -0
  98. data/lib/discorb/modules.rb +56 -14
  99. data/lib/discorb/presence.rb +2 -2
  100. data/lib/discorb/rate_limit.rb +7 -2
  101. data/lib/discorb/reaction.rb +4 -4
  102. data/lib/discorb/role.rb +19 -4
  103. data/lib/discorb/shard.rb +1 -1
  104. data/lib/discorb/sticker.rb +8 -7
  105. data/lib/discorb/user.rb +2 -1
  106. data/lib/discorb/utils/colored_puts.rb +1 -0
  107. data/lib/discorb/voice_state.rb +10 -6
  108. data/lib/discorb/webhook.rb +36 -24
  109. data/lib/discorb.rb +5 -3
  110. data/po/yard.pot +9 -9
  111. data/sig/discorb.rbs +7232 -7235
  112. metadata +21 -5
  113. data/examples/commands/bookmarker.rb +0 -42
  114. data/examples/commands/hello.rb +0 -10
  115. data/examples/commands/inspect.rb +0 -25
@@ -54,7 +54,7 @@ module Discorb
54
54
  attr_reader :shards
55
55
  # @private
56
56
  # @return [Hash{Discorb::Snowflake => Discorb::ApplicationCommand::Command}] The commands on the top level.
57
- attr_reader :bottom_commands
57
+ attr_reader :callable_commands
58
58
  # @private
59
59
  # @return [{String => Thread::Mutex}] A hash of mutexes.
60
60
  attr_reader :mutex
@@ -81,7 +81,8 @@ module Discorb
81
81
  # @param [:debug, :info, :warn, :error, :critical] log_level The log level.
82
82
  # @param [Boolean] wait_until_ready Whether to delay event dispatch until ready.
83
83
  # @param [Boolean] fetch_member Whether to fetch member on ready. This may slow down the client. Default to `false`.
84
- # @param [String] title The title of the process. `false` to default, `nil` to `discorb: User#0000`. Default to `nil`.
84
+ # @param [String] title
85
+ # The title of the process. `false` to default of ruby, `nil` to `discorb: User#0000`. Default to `nil`.
85
86
  #
86
87
  def initialize(
87
88
  allowed_mentions: nil, intents: nil, message_caches: 1000,
@@ -93,7 +94,11 @@ module Discorb
93
94
  @intents = (intents or Intents.default)
94
95
  @events = {}
95
96
  @api_version = nil
96
- @logger = logger || Logger.new($stdout, progname: "discorb")
97
+ @logger = logger || Logger.new(
98
+ $stdout,
99
+ progname: "discorb",
100
+ level: Logger::ERROR,
101
+ )
97
102
  @user = nil
98
103
  @users = Discorb::Dictionary.new
99
104
  @channels = Discorb::Dictionary.new
@@ -108,7 +113,7 @@ module Discorb
108
113
  @tasks = []
109
114
  @conditions = {}
110
115
  @commands = []
111
- @bottom_commands = []
116
+ @callable_commands = []
112
117
  @status = :initialized
113
118
  @fetch_member = fetch_member
114
119
  @title = title
@@ -206,7 +211,11 @@ module Discorb
206
211
  block.call(*args)
207
212
  logger.debug "Dispatched proc with ID #{block.id.inspect}"
208
213
  rescue StandardError, ScriptError => e
209
- dispatch(:error, event_name, args, e)
214
+ if event_name == :error
215
+ raise e
216
+ else
217
+ dispatch(:error, event_name, args, e)
218
+ end
210
219
  end
211
220
  end
212
221
  end
@@ -276,7 +285,13 @@ module Discorb
276
285
  #
277
286
  def fetch_invite(code, with_count: true, with_expiration: true)
278
287
  Async do
279
- _resp, data = @http.request(Route.new("/invites/#{code}?with_count=#{with_count}&with_expiration=#{with_expiration}", "//invites/:code", :get)).wait
288
+ _resp, data = @http.request(
289
+ Route.new(
290
+ "/invites/#{code}?with_count=#{with_count}&with_expiration=#{with_expiration}",
291
+ "//invites/:code",
292
+ :get
293
+ )
294
+ ).wait
280
295
  Invite.new(self, data, false)
281
296
  end
282
297
  end
@@ -387,6 +402,7 @@ module Discorb
387
402
  case ext
388
403
  when Class
389
404
  raise ArgumentError, "#{ext} is not a extension" unless ext < Discorb::Extension
405
+
390
406
  ins = ext.new(self, ...)
391
407
  when Discorb::Extension
392
408
  ins = ext
@@ -415,13 +431,13 @@ module Discorb
415
431
 
416
432
  cls = ins.class
417
433
  cls.loaded(self, ...) if cls.respond_to? :loaded
418
- ins.class.bottom_commands.each do |cmd|
434
+ ins.class.callable_commands.each do |cmd|
419
435
  unless cmd.respond_to? :self_replaced
420
436
  cmd.define_singleton_method(:extension) { ins.class.name }
421
437
  cmd.replace_block(ins)
422
438
  cmd.block.define_singleton_method(:self_replaced) { true }
423
439
  end
424
- @bottom_commands << cmd
440
+ @callable_commands << cmd
425
441
  end
426
442
  @extensions[ins.class.name] = ins
427
443
  ins
@@ -440,9 +456,10 @@ module Discorb
440
456
  # @note If the token is nil, you should use `discorb run` with the `-e` or `--env` option.
441
457
  #
442
458
  def run(token = nil, shards: nil, shard_count: nil)
443
- token ||= ENV["DISCORB_CLI_TOKEN"]
459
+ token ||= ENV.fetch("DISCORB_CLI_TOKEN", nil)
444
460
  raise ArgumentError, "Token is not specified, and -e/--env is not specified" if token.nil?
445
- case ENV["DISCORB_CLI_FLAG"]
461
+
462
+ case ENV.fetch("DISCORB_CLI_FLAG", nil)
446
463
  when nil
447
464
  start_client(token, shards: shards, shard_count: shard_count)
448
465
  when "run"
@@ -490,18 +507,19 @@ module Discorb
490
507
 
491
508
  def before_run(token)
492
509
  require "json"
493
- options = JSON.parse(ENV["DISCORB_CLI_OPTIONS"], symbolize_names: true)
510
+ options = JSON.parse(ENV.fetch("DISCORB_CLI_OPTIONS", nil), symbolize_names: true)
494
511
  setup_commands(token) if options[:setup]
495
512
  end
496
513
 
497
514
  def run_setup(token)
498
515
  guild_ids = "global"
499
- if guilds = ENV["DISCORB_SETUP_GUILDS"]
516
+ if guilds = ENV.fetch("DISCORB_SETUP_GUILDS", nil)
500
517
  guild_ids = guilds.split(",")
501
518
  end
502
519
  guild_ids = false if guild_ids == ["global"]
503
520
  setup_commands(token, guild_ids: guild_ids).wait
504
- if ENV["DISCORB_SETUP_SCRIPT"] == "true"
521
+ clear_commands(token, ENV.fetch("DISCORB_SETUP_CLEAR_GUILDS", "").split(","))
522
+ if ENV.fetch("DISCORB_SETUP_SCRIPT", nil) == "true"
505
523
  @events[:setup]&.each do |event|
506
524
  event.call
507
525
  end
@@ -563,17 +581,11 @@ module Discorb
563
581
  end
564
582
 
565
583
  def main_loop(shard)
566
- close_condition = Async::Condition.new
567
- self.main_task = Async do
568
- set_status(:running, shard)
569
- connect_gateway(false).wait
570
- rescue StandardError
571
- set_status(:running, shard)
572
- close_condition.signal
573
- raise
574
- end
575
- close_condition.wait
576
- main_task.stop
584
+ set_status(:running, shard)
585
+ connect_gateway(false).wait
586
+ rescue StandardError
587
+ set_status(:closed, shard)
588
+ raise
577
589
  end
578
590
 
579
591
  def main_task
@@ -595,13 +607,13 @@ module Discorb
595
607
  def set_default_events
596
608
  on :error, override: true do |event_name, _args, e|
597
609
  message = "An error occurred while dispatching #{event_name}:\n#{e.full_message}"
598
- logger.error message, fallback: $stderr
610
+ logger.error message
599
611
  end
600
612
 
601
613
  once :standby do
602
614
  next if @title == false
603
615
 
604
- title = @title || ENV["DISCORB_CLI_TITLE"] || "discorb: #{@user}"
616
+ title = @title || ENV.fetch("DISCORB_CLI_TITLE", nil) || "discorb: #{@user}"
605
617
  Process.setproctitle title
606
618
  end
607
619
  end
@@ -4,7 +4,7 @@ module Discorb
4
4
  # @return [String] The API base URL.
5
5
  API_BASE_URL = "https://discord.com/api/v10"
6
6
  # @return [String] The version of discorb.
7
- VERSION = "0.16.0"
7
+ VERSION = "0.17.0"
8
8
  # @return [Array<Integer>] The version array of discorb.
9
9
  VERSION_ARRAY = VERSION.split(".").map(&:to_i).freeze
10
10
  # @return [String] The user agent for the bot.
@@ -152,6 +152,7 @@ module Discorb
152
152
  def major_param
153
153
  param_type = @key.split("/").find { |k| k.start_with?(":") }
154
154
  return "" unless param_type
155
+
155
156
  param = url.gsub(API_BASE_URL, "").split("/")[@key.split("/").index(param_type) - 1]
156
157
  %w[:channel_id :guild_id :webhook_id].include?(param_type) ? param : ""
157
158
  end
@@ -53,7 +53,8 @@ module Discorb
53
53
  #
54
54
  # Converts the button to a hash.
55
55
  #
56
- # @see https://discord.com/developers/docs/interactions/message-components#button-object-button-structure Official Discord API docs
56
+ # @see https://discord.com/developers/docs/interactions/message-components#button-object-button-structure
57
+ # Official Discord API docs
57
58
  # @return [Hash] A hash representation of the button.
58
59
  #
59
60
  def to_hash
@@ -37,7 +37,8 @@ module Discorb
37
37
  #
38
38
  # Converts the select menu to a hash.
39
39
  #
40
- # @see https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure Official Discord API docs
40
+ # @see https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure
41
+ # Official Discord API docs
41
42
  # @return [Hash] A hash representation of the select menu.
42
43
  #
43
44
  def to_hash
@@ -109,7 +110,8 @@ module Discorb
109
110
  #
110
111
  # Converts the option to a hash.
111
112
  #
112
- # @see https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-option-structure Official Discord API docs
113
+ # @see https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-option-structure
114
+ # Official Discord API docs
113
115
  # @return [Hash] Hash representation of the option.
114
116
  #
115
117
  def to_hash
@@ -40,7 +40,16 @@ module Discorb
40
40
  # @param [String, nil] value The prefilled value of the text input.
41
41
  # @param [String, nil] placeholder The placeholder of the text input.
42
42
  #
43
- def initialize(label, custom_id, style, min_length: nil, max_length: nil, required: false, value: nil, placeholder: nil)
43
+ def initialize(
44
+ label,
45
+ custom_id,
46
+ style,
47
+ min_length: nil,
48
+ max_length: nil,
49
+ required: false,
50
+ value: nil,
51
+ placeholder: nil
52
+ )
44
53
  @label = label
45
54
  @custom_id = custom_id
46
55
  @style = style
@@ -54,7 +63,8 @@ module Discorb
54
63
  #
55
64
  # Converts the select menu to a hash.
56
65
  #
57
- # @see https://discord.com/developers/docs/interactions/message-components#text-inputs-text-input-structure Official Discord API docs
66
+ # @see https://discord.com/developers/docs/interactions/message-components#text-inputs-text-input-structure
67
+ # Official Discord API docs
58
68
  # @return [Hash] A hash representation of the text input.
59
69
  #
60
70
  def to_hash
@@ -63,4 +63,4 @@ end
63
63
 
64
64
  %w[button text_input select_menu].each do |name|
65
65
  require_relative "components/#{name}"
66
- end
66
+ end
data/lib/discorb/embed.rb CHANGED
@@ -44,8 +44,18 @@ module Discorb
44
44
  # @param [Discorb::Embed::Image, String] image The image of embed.
45
45
  # @param [Discorb::Embed::Thumbnail, String] thumbnail The thumbnail of embed.
46
46
  #
47
- def initialize(title = nil, description = nil, color: nil, url: nil, timestamp: nil, author: nil,
48
- fields: nil, footer: nil, image: nil, thumbnail: nil)
47
+ def initialize(
48
+ title = nil,
49
+ description = nil,
50
+ color: nil,
51
+ url: nil,
52
+ timestamp: nil,
53
+ author: nil,
54
+ fields: nil,
55
+ footer: nil,
56
+ image: nil,
57
+ thumbnail: nil
58
+ )
49
59
  @title = title
50
60
  @description = description
51
61
  @url = url
@@ -161,7 +171,8 @@ module Discorb
161
171
  #
162
172
  # Convert author to hash.
163
173
  #
164
- # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure Offical Discord API Docs
174
+ # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure
175
+ # Offical Discord API Docs
165
176
  # @return [Hash] Converted author.
166
177
  #
167
178
  def to_hash
@@ -193,7 +204,8 @@ module Discorb
193
204
  #
194
205
  # Convert footer to hash.
195
206
  #
196
- # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure Offical Discord API Docs
207
+ # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure
208
+ # Offical Discord API Docs
197
209
  # @return [Hash] Converted footer.
198
210
  #
199
211
  def to_hash
@@ -231,7 +243,8 @@ module Discorb
231
243
  #
232
244
  # Convert field to hash.
233
245
  #
234
- # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure Offical Discord API Docs
246
+ # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure
247
+ # Offical Discord API Docs
235
248
  # @return [Hash] Converted field.
236
249
  #
237
250
  def to_hash
@@ -279,7 +292,8 @@ module Discorb
279
292
  #
280
293
  # Convert image to hash for sending.
281
294
  #
282
- # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure Offical Discord API Docs
295
+ # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure
296
+ # Offical Discord API Docs
283
297
  # @return [Hash] Converted image.
284
298
  #
285
299
  def to_hash
@@ -323,7 +337,8 @@ module Discorb
323
337
  #
324
338
  # Convert thumbnail to hash for sending.
325
339
  #
326
- # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure Offical Discord API Docs
340
+ # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure
341
+ # Offical Discord API Docs
327
342
  # @return [Hash] Converted thumbnail.
328
343
  #
329
344
  def to_hash
data/lib/discorb/emoji.rb CHANGED
@@ -103,7 +103,10 @@ module Discorb
103
103
  payload = {}
104
104
  payload[:name] = name if name != Discorb::Unset
105
105
  payload[:roles] = roles.map { |r| Discorb::Utils.try(r, :id) } if roles != Discorb::Unset
106
- @client.http.request(Route.new("/guilds/#{@guild.id}/emojis/#{@id}", "//guilds/:guild_id/emojis/:emoji_id", :patch), payload, audit_log_reason: reason)
106
+ @client.http.request(
107
+ Route.new("/guilds/#{@guild.id}/emojis/#{@id}", "//guilds/:guild_id/emojis/:emoji_id",
108
+ :patch), payload, audit_log_reason: reason,
109
+ )
107
110
  self
108
111
  end
109
112
  end
@@ -120,7 +123,10 @@ module Discorb
120
123
  #
121
124
  def delete!(reason: nil)
122
125
  Async do
123
- @client.http.request(Route.new("/guilds/#{@guild.id}/emojis/#{@id}", "//guilds/:guild_id/emojis/:emoji_id", :delete), audit_log_reason: reason).wait
126
+ @client.http.request(
127
+ Route.new("/guilds/#{@guild.id}/emojis/#{@id}", "//guilds/:guild_id/emojis/:emoji_id",
128
+ :delete), {}, audit_log_reason: reason,
129
+ ).wait
124
130
  @available = false
125
131
  self
126
132
  end
@@ -230,10 +236,31 @@ module Discorb
230
236
  elsif EmojiTable::UNICODE_TO_DISCORD.key?(name)
231
237
  @name = EmojiTable::UNICODE_TO_DISCORD[name][0]
232
238
  @value = name
239
+ elsif EmojiTable::SKIN_TONES.any? { |t| name.include?(t) }
240
+ name2 = name.dup
241
+ EmojiTable::SKIN_TONES.each.with_index do |t, i|
242
+ next unless name2.include?(t)
243
+
244
+ @skin_tone = i
245
+ name2.sub!(t, "")
246
+ break
247
+ end
248
+ raise ArgumentError, "Invalid skin tone: #{tone}" unless @skin_tone
249
+
250
+ @name = EmojiTable::UNICODE_TO_DISCORD[name2]
251
+ @value = name
233
252
  else
234
253
  raise ArgumentError, "No such emoji: #{name}"
235
254
  end
236
- @value += EmojiTable::SKIN_TONES[tone] if tone.positive?
255
+ if tone.positive?
256
+ unless @value = EmojiTable::DISCORD_TO_UNICODE["#{name}_tone#{tone}"]
257
+ raise ArgumentError,
258
+ "Invalid skin tone for emoji: #{name}"
259
+ end
260
+
261
+ @name = "#{name}_tone#{tone}"
262
+ @skin_tone = tone
263
+ end
237
264
  end
238
265
 
239
266
  # @return [String] The unicode string of the emoji.