discorb 0.13.2 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/build_version.yml +1 -1
  4. data/.github/workflows/codeql-analysis.yml +70 -0
  5. data/.github/workflows/lint-push.yml +20 -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 +34 -0
  10. data/Gemfile +7 -3
  11. data/README.md +1 -1
  12. data/Rakefile +22 -22
  13. data/discorb.gemspec +13 -1
  14. data/docs/faq.md +8 -8
  15. data/examples/commands/bookmarker.rb +2 -1
  16. data/examples/commands/hello.rb +1 -0
  17. data/examples/commands/inspect.rb +3 -2
  18. data/examples/components/authorization_button.rb +2 -1
  19. data/examples/components/select_menu.rb +2 -1
  20. data/examples/extension/main.rb +1 -0
  21. data/examples/extension/message_expander.rb +1 -0
  22. data/examples/simple/eval.rb +3 -2
  23. data/examples/simple/ping_pong.rb +1 -0
  24. data/examples/simple/rolepanel.rb +1 -0
  25. data/examples/simple/wait_for_message.rb +4 -3
  26. data/exe/discorb +8 -7
  27. data/lib/discorb/allowed_mentions.rb +64 -0
  28. data/lib/discorb/app_command/command.rb +274 -0
  29. data/lib/discorb/app_command/handler.rb +168 -0
  30. data/lib/discorb/app_command.rb +2 -404
  31. data/lib/discorb/asset.rb +3 -1
  32. data/lib/discorb/{file.rb → attachment.rb} +42 -35
  33. data/lib/discorb/audit_logs.rb +3 -3
  34. data/lib/discorb/channel.rb +65 -61
  35. data/lib/discorb/client.rb +36 -33
  36. data/lib/discorb/common.rb +29 -22
  37. data/lib/discorb/components/button.rb +106 -0
  38. data/lib/discorb/components/select_menu.rb +157 -0
  39. data/lib/discorb/components/text_input.rb +96 -0
  40. data/lib/discorb/components.rb +11 -276
  41. data/lib/discorb/dictionary.rb +13 -2
  42. data/lib/discorb/embed.rb +40 -33
  43. data/lib/discorb/emoji.rb +21 -5
  44. data/lib/discorb/emoji_table.rb +1 -1
  45. data/lib/discorb/error.rb +4 -6
  46. data/lib/discorb/event.rb +13 -11
  47. data/lib/discorb/exe/about.rb +1 -0
  48. data/lib/discorb/exe/irb.rb +4 -3
  49. data/lib/discorb/exe/new.rb +6 -7
  50. data/lib/discorb/exe/run.rb +2 -1
  51. data/lib/discorb/exe/setup.rb +8 -5
  52. data/lib/discorb/exe/show.rb +1 -0
  53. data/lib/discorb/extend.rb +19 -14
  54. data/lib/discorb/extension.rb +5 -1
  55. data/lib/discorb/gateway.rb +82 -29
  56. data/lib/discorb/guild.rb +58 -80
  57. data/lib/discorb/guild_template.rb +5 -5
  58. data/lib/discorb/http.rb +52 -170
  59. data/lib/discorb/integration.rb +32 -3
  60. data/lib/discorb/intents.rb +9 -4
  61. data/lib/discorb/interaction/autocomplete.rb +5 -4
  62. data/lib/discorb/interaction/command.rb +34 -9
  63. data/lib/discorb/interaction/components.rb +5 -2
  64. data/lib/discorb/interaction/modal.rb +33 -0
  65. data/lib/discorb/interaction/response.rb +41 -12
  66. data/lib/discorb/interaction/root.rb +1 -0
  67. data/lib/discorb/interaction.rb +2 -1
  68. data/lib/discorb/invite.rb +1 -1
  69. data/lib/discorb/log.rb +4 -3
  70. data/lib/discorb/member.rb +4 -6
  71. data/lib/discorb/message.rb +38 -241
  72. data/lib/discorb/message_meta.rb +157 -0
  73. data/lib/discorb/modules.rb +47 -23
  74. data/lib/discorb/permission.rb +2 -2
  75. data/lib/discorb/presence.rb +6 -3
  76. data/lib/discorb/rate_limit.rb +15 -21
  77. data/lib/discorb/role.rb +3 -3
  78. data/lib/discorb/sticker.rb +2 -2
  79. data/lib/discorb/user.rb +3 -3
  80. data/lib/discorb/utils/colored_puts.rb +1 -0
  81. data/lib/discorb/voice_state.rb +7 -2
  82. data/lib/discorb/webhook.rb +9 -6
  83. data/lib/discorb.rb +2 -1
  84. data/sig/discorb.rbs +5836 -6714
  85. data/template-replace/scripts/arrow.rb +1 -0
  86. data/template-replace/scripts/favicon.rb +1 -0
  87. data/template-replace/scripts/index.rb +2 -1
  88. data/template-replace/scripts/locale_ja.rb +5 -4
  89. data/template-replace/scripts/sidebar.rb +1 -0
  90. data/template-replace/scripts/version.rb +7 -10
  91. data/template-replace/scripts/yard_replace.rb +5 -4
  92. metadata +30 -5
data/lib/discorb/embed.rb CHANGED
@@ -45,45 +45,46 @@ module Discorb
45
45
  # @param [Discorb::Embed::Thumbnail, String] thumbnail The thumbnail of embed.
46
46
  #
47
47
  def initialize(title = nil, description = nil, color: nil, url: nil, timestamp: nil, author: nil,
48
- fields: nil, footer: nil, image: nil, thumbnail: nil, data: nil)
49
- if data.nil?
50
- @title = title
51
- @description = description
52
- @url = url
53
- @timestamp = timestamp
54
- @color = color
55
- @author = author
56
- @fields = fields || []
57
- @footer = footer
58
- @image = image && (image.is_a?(String) ? Image.new(image) : image)
59
- @thumbnail = thumbnail && (thumbnail.is_a?(String) ? Thumbnail.new(thumbnail) : thumbnail)
60
- @type = "rich"
61
- else
62
- @title = data[:title]
63
- @description = data[:description]
64
- @url = data[:url]
65
- @timestamp = data[:timestamp] && Time.iso8601(data[:timestamp])
66
- @type = data[:type]
67
- @color = data[:color] && Color.new(data[:color])
68
- @footer = data[:footer] && Footer.new(data[:footer][:text], icon: data[:footer][:icon_url])
69
- @author = if data[:author]
70
- Author.new(data[:author][:name], icon: data[:author][:icon_url],
71
- url: data[:author][:url])
72
- end
73
- @thumbnail = data[:thumbnail] && Thumbnail.new(data[:thumbnail])
74
- @image = data[:image] && Image.new(data[:image])
75
- @video = data[:video] && Video.new(data[:video])
76
- @provider = data[:provider] && Provider.new(data[:provider])
77
- @fields = data[:fields] ? data[:fields].map { |f| Field.new(f[:name], f[:value], inline: f[:inline]) } : []
78
- end
48
+ fields: nil, footer: nil, image: nil, thumbnail: nil)
49
+ @title = title
50
+ @description = description
51
+ @url = url
52
+ @timestamp = timestamp
53
+ @color = color
54
+ @author = author
55
+ @fields = fields || []
56
+ @footer = footer
57
+ @image = image && (image.is_a?(String) ? Image.new(image) : image)
58
+ @thumbnail = thumbnail && (thumbnail.is_a?(String) ? Thumbnail.new(thumbnail) : thumbnail)
59
+ @type = "rich"
60
+ end
61
+
62
+ # @private
63
+ def initialize_hash(data)
64
+ @title = data[:title]
65
+ @description = data[:description]
66
+ @url = data[:url]
67
+ @timestamp = data[:timestamp] && Time.iso8601(data[:timestamp])
68
+ @type = data[:type]
69
+ @color = data[:color] && Color.new(data[:color])
70
+ @footer = data[:footer] && Footer.new(data[:footer][:text], icon: data[:footer][:icon_url])
71
+ @author = if data[:author]
72
+ Author.new(data[:author][:name], icon: data[:author][:icon_url],
73
+ url: data[:author][:url])
74
+ end
75
+ @thumbnail = data[:thumbnail] && Thumbnail.new(data[:thumbnail])
76
+ @image = data[:image] && Image.new(data[:image])
77
+ @video = data[:video] && Video.new(data[:video])
78
+ @provider = data[:provider] && Provider.new(data[:provider])
79
+ @fields = data[:fields] ? data[:fields].map { |f| Field.new(f[:name], f[:value], inline: f[:inline]) } : []
79
80
  end
80
81
 
81
82
  def image=(value)
82
- @image = (value.is_a?(String)) ? Image.new(value) : value
83
+ @image = value.is_a?(String) ? Image.new(value) : value
83
84
  end
84
85
 
85
86
  def thumbnail=(value)
86
- @thumbnail = (value.is_a?(String)) ? Thumbnail.new(value) : value
87
+ @thumbnail = value.is_a?(String) ? Thumbnail.new(value) : value
87
88
  end
88
89
 
89
90
  def inspect
@@ -111,6 +112,12 @@ module Discorb
111
112
  ret
112
113
  end
113
114
 
115
+ def self.from_hash(data)
116
+ inst = allocate
117
+ inst.initialize_hash(data)
118
+ inst
119
+ end
120
+
114
121
  #
115
122
  # Represents an author of embed.
116
123
  #
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