discorb 0.13.4 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/codeql-analysis.yml +70 -0
  4. data/.github/workflows/lint-push.yml +18 -0
  5. data/.github/workflows/lint.yml +16 -0
  6. data/.rubocop.yml +70 -0
  7. data/Changelog.md +12 -0
  8. data/Gemfile +7 -3
  9. data/Rakefile +22 -22
  10. data/discorb.gemspec +1 -0
  11. data/examples/commands/bookmarker.rb +2 -1
  12. data/examples/commands/hello.rb +1 -0
  13. data/examples/commands/inspect.rb +3 -2
  14. data/examples/components/authorization_button.rb +2 -1
  15. data/examples/components/select_menu.rb +2 -1
  16. data/examples/extension/main.rb +1 -0
  17. data/examples/extension/message_expander.rb +1 -0
  18. data/examples/simple/eval.rb +3 -2
  19. data/examples/simple/ping_pong.rb +1 -0
  20. data/examples/simple/rolepanel.rb +1 -0
  21. data/examples/simple/wait_for_message.rb +4 -3
  22. data/exe/discorb +8 -7
  23. data/lib/discorb/allowed_mentions.rb +64 -0
  24. data/lib/discorb/app_command/command.rb +274 -0
  25. data/lib/discorb/app_command/handler.rb +168 -0
  26. data/lib/discorb/app_command.rb +2 -426
  27. data/lib/discorb/asset.rb +2 -0
  28. data/lib/discorb/audit_logs.rb +3 -3
  29. data/lib/discorb/channel.rb +19 -4
  30. data/lib/discorb/client.rb +30 -27
  31. data/lib/discorb/common.rb +4 -26
  32. data/lib/discorb/components/button.rb +106 -0
  33. data/lib/discorb/components/select_menu.rb +157 -0
  34. data/lib/discorb/components/text_input.rb +96 -0
  35. data/lib/discorb/components.rb +11 -276
  36. data/lib/discorb/dictionary.rb +3 -0
  37. data/lib/discorb/embed.rb +2 -2
  38. data/lib/discorb/emoji.rb +19 -3
  39. data/lib/discorb/emoji_table.rb +1 -1
  40. data/lib/discorb/error.rb +4 -6
  41. data/lib/discorb/event.rb +9 -7
  42. data/lib/discorb/exe/about.rb +1 -0
  43. data/lib/discorb/exe/irb.rb +4 -3
  44. data/lib/discorb/exe/new.rb +6 -7
  45. data/lib/discorb/exe/run.rb +2 -1
  46. data/lib/discorb/exe/setup.rb +8 -5
  47. data/lib/discorb/exe/show.rb +1 -0
  48. data/lib/discorb/extend.rb +19 -14
  49. data/lib/discorb/extension.rb +5 -1
  50. data/lib/discorb/gateway.rb +28 -30
  51. data/lib/discorb/guild.rb +11 -13
  52. data/lib/discorb/guild_template.rb +2 -2
  53. data/lib/discorb/http.rb +15 -17
  54. data/lib/discorb/integration.rb +1 -1
  55. data/lib/discorb/intents.rb +1 -1
  56. data/lib/discorb/interaction/autocomplete.rb +4 -3
  57. data/lib/discorb/interaction/command.rb +34 -9
  58. data/lib/discorb/interaction/components.rb +5 -2
  59. data/lib/discorb/interaction/modal.rb +33 -0
  60. data/lib/discorb/interaction/response.rb +33 -4
  61. data/lib/discorb/interaction/root.rb +1 -0
  62. data/lib/discorb/interaction.rb +2 -1
  63. data/lib/discorb/log.rb +1 -1
  64. data/lib/discorb/member.rb +1 -3
  65. data/lib/discorb/message.rb +26 -277
  66. data/lib/discorb/message_meta.rb +205 -0
  67. data/lib/discorb/modules.rb +1 -1
  68. data/lib/discorb/permission.rb +2 -2
  69. data/lib/discorb/presence.rb +4 -1
  70. data/lib/discorb/rate_limit.rb +2 -4
  71. data/lib/discorb/user.rb +1 -1
  72. data/lib/discorb/utils/colored_puts.rb +1 -0
  73. data/lib/discorb/voice_state.rb +3 -0
  74. data/lib/discorb/webhook.rb +1 -1
  75. data/lib/discorb.rb +1 -0
  76. data/template-replace/scripts/arrow.rb +1 -0
  77. data/template-replace/scripts/favicon.rb +1 -0
  78. data/template-replace/scripts/index.rb +2 -1
  79. data/template-replace/scripts/locale_ja.rb +5 -4
  80. data/template-replace/scripts/sidebar.rb +1 -0
  81. data/template-replace/scripts/version.rb +7 -10
  82. data/template-replace/scripts/yard_replace.rb +5 -4
  83. metadata +16 -2
@@ -1,428 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
- module Discorb
4
- #
5
- # Handles application commands.
6
- #
7
- module ApplicationCommand
8
- #
9
- # Module to handle application commands.
10
- #
11
- module Handler
12
- #
13
- # Add new top-level command.
14
- #
15
- # @param [String] command_name Command name.
16
- # @param [String] description Command description.
17
- # @param [Hash{String => Hash{:description => String, :optional => Boolean, :type => Object}}] options Command options.
18
- # The key is the option name, the value is a hash with the following keys:
19
- #
20
- # | Key | Type | Description |
21
- # | --- | --- | --- |
22
- # | `:description` | `String` | Description of the option. |
23
- # | `:required` | Whether the argument is required. `optional` will be used if not specified. |
24
- # | `:optional` | Whether the argument is optional. `required` will be used if not specified. |
25
- # | `:type` | `Object` | Type of the option. |
26
- # | `:choice` | `Hash{String => String, Integer, Float}` | Type of the option. |
27
- # | `:default` | `Object` | Default value of the option. |
28
- # | `:channel_types` | `Array<Class<Discorb::Channel>>` | Type of the channel option. |
29
- # | `:autocomplete` | `Proc` | Autocomplete function. |
30
- # | `:range` | `Range` | Range of the option. Only valid for numeric options. (`:int`, `:float`) |
31
- #
32
- # @param [Array<#to_s>, false, nil] guild_ids Guild IDs to set the command to. `false` to global command, `nil` to use default.
33
- # @param [Proc] block Command block.
34
- #
35
- # @return [Discorb::ApplicationCommand::Command::SlashCommand] Command object.
36
- #
37
- # @see file:docs/application_command.md#register-slash-command Application Comamnds: Register Slash Command
38
- # @see file:docs/cli/setup.md CLI: setup
39
- #
40
- def slash(command_name, description, options = {}, guild_ids: nil, &block)
41
- command = Discorb::ApplicationCommand::Command::SlashCommand.new(command_name, description, options, guild_ids, block, 1, "")
42
- @commands << command
43
- @bottom_commands << command
44
- command
45
- end
46
-
47
- #
48
- # Add new command with group.
49
- #
50
- # @param [String] command_name Command name.
51
- # @param [String] description Command description.
52
- # @param [Array<#to_s>, false, nil] guild_ids Guild IDs to set the command to. `false` to global command, `nil` to use default.
53
- #
54
- # @yield Block to yield with the command.
55
- # @yieldparam [Discorb::ApplicationCommand::Command::GroupCommand] group Group command.
56
- #
57
- # @return [Discorb::ApplicationCommand::Command::GroupCommand] Command object.
58
- #
59
- # @see file:docs/application_command.md Application Commands
60
- # @see file:docs/cli/setup.md CLI: setup
61
- #
62
- def slash_group(command_name, description, guild_ids: nil, &block)
63
- command = Discorb::ApplicationCommand::Command::GroupCommand.new(command_name, description, guild_ids, nil, self)
64
- command.yield_self(&block) if block_given?
65
- @commands << command
66
- command
67
- end
68
-
69
- #
70
- # Add message context menu command.
71
- #
72
- # @param [String] command_name Command name.
73
- # @param [Array<#to_s>, false, nil] guild_ids Guild IDs to set the command to. `false` to global command, `nil` to use default.
74
- # @param [Proc] block Command block.
75
- # @yield [interaction, message] Block to execute.
76
- # @yieldparam [Discorb::CommandInteraction::UserMenuCommand] interaction Interaction object.
77
- # @yieldparam [Discorb::Message] message Message object.
78
- #
79
- # @return [Discorb::ApplicationCommand::Command] Command object.
80
- #
81
- def message_command(command_name, guild_ids: nil, &block)
82
- command = Discorb::ApplicationCommand::Command.new(command_name, guild_ids, block, 3)
83
- @commands << command
84
- command
85
- end
86
-
87
- #
88
- # Add user context menu command.
89
- #
90
- # @param [String] command_name Command name.
91
- # @param [Array<#to_s>, false, nil] guild_ids Guild IDs to set the command to. `false` to global command, `nil` to use default.
92
- # @param [Proc] block Command block.
93
- # @yield [interaction, user] Block to execute.
94
- # @yieldparam [Discorb::CommandInteraction::UserMenuCommand] interaction Interaction object.
95
- # @yieldparam [Discorb::User] user User object.
96
- #
97
- # @return [Discorb::ApplicationCommand::Command] Command object.
98
- #
99
- def user_command(command_name, guild_ids: nil, &block)
100
- command = Discorb::ApplicationCommand::Command.new(command_name, guild_ids, block, 2)
101
- @commands << command
102
- command
103
- end
104
-
105
- #
106
- # Setup commands.
107
- # @async
108
- # @see Client#initialize
109
- #
110
- # @param [String] token Bot token.
111
- # @param [Array<#to_s>, false, nil] guild_ids Guild IDs to use as default. If `false` is given, it will be global command.
112
- #
113
- # @note `token` parameter only required if you don't run client.
114
- #
115
- def setup_commands(token = nil, guild_ids: nil)
116
- Async do
117
- @token ||= token
118
- @http = HTTP.new(self)
119
- global_commands = @commands.select { |c| c.guild_ids == false or c.guild_ids == [] }
120
- local_commands = @commands.select { |c| c.guild_ids.is_a?(Array) and c.guild_ids.any? }
121
- default_commands = @commands.select { |c| c.guild_ids.nil? }
122
- if guild_ids.is_a?(Array)
123
- default_commands.each do |command|
124
- command.instance_variable_set(:@guild_ids, guild_ids)
125
- end
126
- local_commands += default_commands
127
- else
128
- global_commands += default_commands
129
- end
130
- final_guild_ids = local_commands.map(&:guild_ids).flatten.map(&:to_s).uniq
131
- app_info = fetch_application.wait
132
- @http.request(
133
- Route.new(
134
- "/applications/#{app_info.id}/commands",
135
- "//applications/:application_id/commands",
136
- :put
137
- ),
138
- global_commands.map(&:to_hash)
139
- ).wait unless global_commands.empty?
140
- if ENV["DISCORB_CLI_FLAG"] == "setup"
141
- sputs "Registered commands for global:"
142
- global_commands.each do |command|
143
- iputs "- #{command.name}"
144
- end
145
- end
146
- final_guild_ids.each do |guild_id|
147
- commands = local_commands.select { |c| c.guild_ids.include?(guild_id) }
148
- @http.request(
149
- Route.new("/applications/#{app_info.id}/guilds/#{guild_id}/commands",
150
- "//applications/:application_id/guilds/:guild_id/commands",
151
- :put),
152
- commands.map(&:to_hash)
153
- ).wait
154
- sputs "Registered commands for #{guild_id}:"
155
- commands.each do |command|
156
- iputs "- #{command.name}"
157
- end
158
- end unless final_guild_ids.empty?
159
- @log.info "Successfully setup commands"
160
- end
161
- end
162
- end
163
-
164
- #
165
- # Represents a application command.
166
- # @abstract
167
- #
168
- class Command < DiscordModel
169
- # @return [String] The name of the command.
170
- attr_reader :name
171
- # @return [Array<#to_s>] The guild ids that the command is enabled in.
172
- attr_reader :guild_ids
173
- # @return [Proc] The block of the command.
174
- attr_reader :block
175
- # @return [:chat_input, :user, :message] The type of the command.
176
- attr_reader :type
177
- # @return [Integer] The raw type of the command.
178
- attr_reader :type_raw
179
- # @return [Discorb::Dictionary{Discorb::Snowflake, :global => Discorb::Snowflake}] The ID mapping.
180
- attr_reader :id_map
181
-
182
- @types = {
183
- 1 => :chat_input,
184
- 2 => :user,
185
- 3 => :message,
186
- }.freeze
187
-
188
- # @private
189
- def initialize(name, guild_ids, block, type)
190
- @name = name
191
- @guild_ids = guild_ids&.map(&:to_s)
192
- @block = block
193
- @raw_type = type
194
- @type = Discorb::ApplicationCommand::Command.types[type]
195
- @type_raw = type
196
- @id_map = Discorb::Dictionary.new
197
- end
198
-
199
- # @private
200
- def replace_block(instance)
201
- current_block = @block.dup
202
- @block = Proc.new do |*args|
203
- instance.instance_exec(*args, &current_block)
204
- end
205
- end
206
-
207
- # @private
208
- def to_hash
209
- {
210
- name: @name,
211
- default_permission: @default_permission,
212
- type: @type_raw,
213
- }
214
- end
215
-
216
- #
217
- # Represents the slash command.
218
- #
219
- class SlashCommand < Command
220
- # @return [String] The description of the command.
221
- attr_reader :description
222
- # @return [Hash{String => Hash}] The options of the command.
223
- attr_reader :options
224
-
225
- # @private
226
- def initialize(name, description, options, guild_ids, block, type, parent)
227
- super(name, guild_ids, block, type)
228
- @description = description
229
- @options = options
230
- @id = nil
231
- @parent = parent
232
- @id_map = Discorb::Dictionary.new
233
- end
234
-
235
- #
236
- # Returns the commands name.
237
- #
238
- # @return [String] The name of the command.
239
- #
240
- def to_s
241
- (@parent + " " + @name).strip
242
- end
243
-
244
- # @private
245
- def to_hash
246
- options_payload = options.map do |name, value|
247
- ret = {
248
- type: case value[:type]
249
- when String, :string, :str
250
- 3
251
- when Integer, :integer, :int
252
- 4
253
- when TrueClass, FalseClass, :boolean, :bool
254
- 5
255
- when Discorb::User, Discorb::Member, :user, :member
256
- 6
257
- when Discorb::Channel, :channel
258
- 7
259
- when Discorb::Role, :role
260
- 8
261
- when :mentionable
262
- 9
263
- when Float, :float
264
- 10
265
- else
266
- raise ArgumentError, "Invalid option type: #{value[:type]}"
267
- end,
268
- name: name,
269
- description: value[:description],
270
- required: value[:required].nil? ? !value[:optional] : value[:required],
271
- }
272
-
273
- ret[:choices] = value[:choices].map { |t| { name: t[0], value: t[1] } } if value[:choices]
274
-
275
- ret[:channel_types] = value[:channel_types].map(&:channel_type) if value[:channel_types]
276
-
277
- ret[:autocomplete] = !!value[:autocomplete] if value[:autocomplete]
278
- if value[:range]
279
- ret[:min_value] = value[:range].begin
280
- ret[:max_value] = value[:range].end
281
- end
282
- ret
283
- end
284
- {
285
- name: @name,
286
- default_permission: true,
287
- description: @description,
288
- options: options_payload,
289
- }
290
- end
291
- end
292
-
293
- #
294
- # Represents the command with subcommands.
295
- #
296
- class GroupCommand < Command
297
- # @return [Array<Discorb::ApplicationCommand::Command::SlashCommand, Discorb::ApplicationCommand::Command::SubcommandGroup>] The subcommands of the command.
298
- attr_reader :commands
299
- # @return [String] The description of the command.
300
- attr_reader :description
301
-
302
- # @private
303
- def initialize(name, description, guild_ids, type, client)
304
- super(name, guild_ids, block, type)
305
- @description = description
306
- @commands = []
307
- @client = client
308
- @id_map = Discorb::Dictionary.new
309
- end
310
-
311
- #
312
- # Add new subcommand.
313
- #
314
- # @param (see Discorb::ApplicationCommand::Handler#slash)
315
- # @return [Discorb::ApplicationCommand::Command::SlashCommand] The added subcommand.
316
- #
317
- def slash(command_name, description, options = {}, &block)
318
- command = Discorb::ApplicationCommand::Command::SlashCommand.new(command_name, description, options, [], block, 1, @name)
319
- @client.bottom_commands << command
320
- @commands << command
321
- command
322
- end
323
-
324
- #
325
- # Add new subcommand group.
326
- #
327
- # @param [String] command_name Group name.
328
- # @param [String] description Group description.
329
- #
330
- # @yield Block to yield with the command.
331
- # @yieldparam [Discorb::ApplicationCommand::Command::SubcommandGroup] group Group command.
332
- #
333
- # @return [Discorb::ApplicationCommand::Command::SubcommandGroup] Command object.
334
- #
335
- # @see file:docs/application_command.md Application Commands
336
- #
337
- def group(command_name, description, &block)
338
- command = Discorb::ApplicationCommand::Command::SubcommandGroup.new(command_name, description, @name, @client)
339
- command.yield_self(&block) if block_given?
340
- @commands << command
341
- command
342
- end
343
-
344
- #
345
- # Returns the command name.
346
- #
347
- # @return [String] The command name.
348
- #
349
- def to_s
350
- @name
351
- end
352
-
353
- # @private
354
- def block_replace(instance)
355
- super
356
- @commands.each { |c| c.replace_block(instance) }
357
- end
358
-
359
- # @private
360
- def to_hash
361
- options_payload = @commands.map do |command|
362
- if command.is_a?(SlashCommand)
363
- {
364
- name: command.name,
365
- description: command.description,
366
- default_permission: true,
367
- type: 1,
368
- options: command.to_hash[:options],
369
- }
370
- else
371
- {
372
- name: command.name,
373
- description: command.description,
374
- default_permission: true,
375
- type: 2,
376
- options: command.commands.map { |c| c.to_hash.merge(type: 1) },
377
- }
378
- end
379
- end
380
-
381
- {
382
- name: @name,
383
- default_permission: @enabled,
384
- description: @description,
385
- options: options_payload,
386
- }
387
- end
388
- end
389
-
390
- #
391
- # Represents the subcommand group.
392
- #
393
- class SubcommandGroup < GroupCommand
394
- # @return [Array<Discorb::ApplicationCommand::Command::SlashCommand>] The subcommands of the command.
395
- attr_reader :commands
396
-
397
- # @private
398
- def initialize(name, description, parent, client)
399
- super(name, description, [], 1, client)
400
-
401
- @commands = []
402
- @parent = parent
403
- end
404
-
405
- def to_s
406
- @parent + " " + @name
407
- end
408
-
409
- #
410
- # Add new subcommand.
411
- # @param (see Discorb::ApplicationCommand::Handler#slash)
412
- # @return [Discorb::ApplicationCommand::Command::SlashCommand] The added subcommand.
413
- #
414
- def slash(command_name, description, options = {}, &block)
415
- command = Discorb::ApplicationCommand::Command::SlashCommand.new(command_name, description, options, [], block, 1, @parent + " " + @name)
416
- @commands << command
417
- @client.bottom_commands << command
418
- command
419
- end
420
- end
421
-
422
- class << self
423
- # @private
424
- attr_reader :types
425
- end
426
- end
427
- end
2
+ %w[command handler].each do |file|
3
+ require_relative "app_command/#{file}.rb"
428
4
  end
data/lib/discorb/asset.rb CHANGED
@@ -80,7 +80,9 @@ module Discorb
80
80
  #
81
81
  # @return [String] URL of the avatar.
82
82
  #
83
+ # rubocop: disable Lint/UnusedMethodArgument
83
84
  def url(image_format: nil, size: 1024)
85
+ # rubocop: enable Lint/UnusedMethodArgument
84
86
  "https://cdn.discordapp.com/embed/avatars/#{@discriminator.to_i % 5}.png"
85
87
  end
86
88
 
@@ -226,7 +226,7 @@ module Discorb
226
226
  # @private
227
227
  #
228
228
  def initialize(data)
229
- @data = data.map { |d| [d[:key].to_sym, d] }.to_h
229
+ @data = data.to_h { |d| [d[:key].to_sym, d] }
230
230
  @data.each do |k, v|
231
231
  define_singleton_method(k) { Change.new(v) }
232
232
  end
@@ -287,8 +287,8 @@ module Discorb
287
287
  else
288
288
  ->(v) { v }
289
289
  end
290
- @old_value = method.(data[:old_value])
291
- @new_value = method.(data[:new_value])
290
+ @old_value = method.call(data[:old_value])
291
+ @new_value = method.call(data[:new_value])
292
292
  end
293
293
 
294
294
  #
@@ -211,9 +211,9 @@ module Discorb
211
211
  @guild_id = data[:guild_id]
212
212
  @position = data[:position]
213
213
  @permission_overwrites = if data[:permission_overwrites]
214
- data[:permission_overwrites].map do |ow|
214
+ data[:permission_overwrites].to_h do |ow|
215
215
  [(ow[:type] == 1 ? guild.roles : guild.members)[ow[:id]], PermissionOverwrite.new(ow[:allow], ow[:deny])]
216
- end.to_h
216
+ end
217
217
  else
218
218
  {}
219
219
  end
@@ -759,7 +759,7 @@ module Discorb
759
759
  end
760
760
 
761
761
  def audiences
762
- voice_states.filter { |state| state.suppress? }.map(&:member)
762
+ voice_states.filter(&:suppress?).map(&:member)
763
763
  end
764
764
 
765
765
  private
@@ -990,14 +990,23 @@ module Discorb
990
990
  end
991
991
  end
992
992
 
993
+ #
994
+ # Represents a thread in news channel(aka announcement channel).
995
+ #
993
996
  class News < ThreadChannel
994
997
  @channel_type = 10
995
998
  end
996
999
 
1000
+ #
1001
+ # Represents a public thread in text channel.
1002
+ #
997
1003
  class Public < ThreadChannel
998
1004
  @channel_type = 11
999
1005
  end
1000
1006
 
1007
+ #
1008
+ # Represents a private thread in text channel.
1009
+ #
1001
1010
  class Private < ThreadChannel
1002
1011
  @channel_type = 12
1003
1012
  end
@@ -1007,7 +1016,7 @@ module Discorb
1007
1016
  end
1008
1017
 
1009
1018
  #
1010
- # Repre
1019
+ # Represents a member in a thread.
1011
1020
  #
1012
1021
  class Member < DiscordModel
1013
1022
  attr_reader :joined_at
@@ -1059,6 +1068,9 @@ module Discorb
1059
1068
  end
1060
1069
  end
1061
1070
 
1071
+ #
1072
+ # Represents a category in a guild.
1073
+ #
1062
1074
  class CategoryChannel < GuildChannel
1063
1075
  @channel_type = 4
1064
1076
 
@@ -1099,6 +1111,9 @@ module Discorb
1099
1111
  end
1100
1112
  end
1101
1113
 
1114
+ #
1115
+ # Represents a DM channel.
1116
+ #
1102
1117
  class DMChannel < Channel
1103
1118
  include Messageable
1104
1119
 
@@ -186,10 +186,8 @@ module Discorb
186
186
  @log.debug "Dispatching event #{event_name}"
187
187
  events.each do |block|
188
188
  Async do
189
- Async(annotation: "Discorb event: #{event_name}") do |task|
190
- if block.is_a?(Discorb::EventHandler)
191
- @events[event_name].delete(block) if block.metadata[:once]
192
- end
189
+ Async(annotation: "Discorb event: #{event_name}") do |_task|
190
+ @events[event_name].delete(block) if block.is_a?(Discorb::EventHandler) && block.metadata[:once]
193
191
  block.call(*args)
194
192
  @log.debug "Dispatched proc with ID #{block.id.inspect}"
195
193
  rescue StandardError, ScriptError => e
@@ -261,7 +259,7 @@ module Discorb
261
259
  #
262
260
  # @return [Async::Task<Discorb::Invite>] The invite.
263
261
  #
264
- def fetch_invite(code, with_count: false, with_expiration: false)
262
+ def fetch_invite(code, with_count: true, with_expiration: true)
265
263
  Async do
266
264
  _resp, data = @http.request(Route.new("/invites/#{code}?with_count=#{with_count}&with_expiration=#{with_expiration}", "//invites/:code", :get)).wait
267
265
  Invite.new(self, data, false)
@@ -295,8 +293,8 @@ module Discorb
295
293
  #
296
294
  def fetch_nitro_sticker_packs
297
295
  Async do
298
- _resp, data = @http.request(Route.new("/stickers-packs", "//stickers-packs", :get)).wait
299
- data.map { |pack| Sticker::Pack.new(self, pack) }
296
+ _resp, data = @http.request(Route.new("/sticker-packs", "//sticker-packs", :get)).wait
297
+ data[:sticker_packs].map { |pack| Sticker::Pack.new(self, pack) }
300
298
  end
301
299
  end
302
300
 
@@ -305,18 +303,14 @@ module Discorb
305
303
  #
306
304
  # @param [Discorb::Activity] activity The activity to update.
307
305
  # @param [:online, :idle, :dnd, :invisible] status The status to update.
308
- # @param [String] afk Whether to set the client as AFK.
309
306
  #
310
- def update_presence(activity = nil, status: nil, afk: false)
307
+ def update_presence(activity = nil, status: nil)
311
308
  payload = {
312
309
  activities: [],
313
310
  status: status,
314
- afk: nil,
315
311
  since: nil,
316
312
  }
317
- if !activity.nil?
318
- payload[:activities] = [activity.to_hash]
319
- end
313
+ payload[:activities] = [activity.to_hash] unless activity.nil?
320
314
  payload[:status] = status unless status.nil?
321
315
  if @connection
322
316
  Async do
@@ -374,10 +368,11 @@ module Discorb
374
368
  # @param [Object] ... The arguments to pass to the `ext#initialize`.
375
369
  #
376
370
  def load_extension(ext, ...)
377
- if ext.is_a?(Class)
371
+ case ext
372
+ when Class
378
373
  raise ArgumentError, "#{ext} is not a extension" unless ext < Discorb::Extension
379
374
  ins = ext.new(self, ...)
380
- elsif ext.is_a?(Discorb::Extension)
375
+ when Discorb::Extension
381
376
  ins = ext
382
377
  else
383
378
  raise ArgumentError, "#{ext} is not a extension"
@@ -398,12 +393,20 @@ module Discorb
398
393
  ins.class.commands.each do |cmd|
399
394
  cmd.define_singleton_method(:extension) { ins.class.name }
400
395
  cmd.replace_block(ins)
396
+ cmd.block.define_singleton_method(:self_replaced) { true }
401
397
  @commands << cmd
402
398
  end
403
399
 
404
400
  cls = ins.class
405
401
  cls.loaded(self, ...) if cls.respond_to? :loaded
406
- @bottom_commands += ins.class.bottom_commands
402
+ ins.class.bottom_commands.each do |cmd|
403
+ unless cmd.respond_to? :self_replaced
404
+ cmd.define_singleton_method(:extension) { ins.class.name }
405
+ cmd.replace_block(ins)
406
+ cmd.block.define_singleton_method(:self_replaced) { true }
407
+ end
408
+ @bottom_commands << cmd
409
+ end
407
410
  @extensions[ins.class.name] = ins
408
411
  ins
409
412
  end
@@ -463,7 +466,7 @@ module Discorb
463
466
  ::File.open(options[:log_file], "a")
464
467
  end
465
468
  @log.level = options[:log_level].to_sym
466
- @log.colorize_log = options[:log_color] == nil ? @log.out.isatty : options[:log_color]
469
+ @log.colorize_log = options[:log_color].nil? ? @log.out.isatty : options[:log_color]
467
470
  end
468
471
  end
469
472
  end
@@ -473,29 +476,29 @@ module Discorb
473
476
  if guilds = ENV["DISCORB_SETUP_GUILDS"]
474
477
  guild_ids = guilds.split(",")
475
478
  end
476
- if guild_ids == ["global"]
477
- guild_ids = false
478
- end
479
+ guild_ids = false if guild_ids == ["global"]
479
480
  setup_commands(token, guild_ids: guild_ids).wait
480
- @events[:setup]&.each do |event|
481
- event.call
481
+ if ENV["DISCORB_SETUP_SCRIPT"] == "true"
482
+ @events[:setup]&.each do |event|
483
+ event.call
484
+ end
485
+ self.on_setup if respond_to? :on_setup
482
486
  end
483
- self.on_setup if respond_to? :on_setup
484
487
  end
485
488
 
486
489
  def start_client(token)
487
- Async do |task|
488
- Signal.trap(:SIGINT) {
490
+ Async do |_task|
491
+ Signal.trap(:SIGINT) do
489
492
  @log.info "SIGINT received, closing..."
490
493
  Signal.trap(:SIGINT, "DEFAULT")
491
494
  close!
492
- }
495
+ end
493
496
  @token = token.to_s
494
497
  @close_condition = Async::Condition.new
495
498
  @main_task = Async do
496
499
  @status = :running
497
500
  connect_gateway(false).wait
498
- rescue
501
+ rescue StandardError
499
502
  @status = :stopped
500
503
  @close_condition.signal
501
504
  raise