discorb 0.13.4 → 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 (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
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Handles application commands.
6
+ #
7
+ module ApplicationCommand
8
+ #
9
+ # Represents a application command.
10
+ # @abstract
11
+ #
12
+ class Command < DiscordModel
13
+ # @return [String] The name of the command.
14
+ attr_reader :name
15
+ # @return [Array<#to_s>] The guild ids that the command is enabled in.
16
+ attr_reader :guild_ids
17
+ # @return [Proc] The block of the command.
18
+ attr_reader :block
19
+ # @return [:chat_input, :user, :message] The type of the command.
20
+ attr_reader :type
21
+ # @return [Integer] The raw type of the command.
22
+ attr_reader :type_raw
23
+ # @return [Discorb::Dictionary{Discorb::Snowflake, :global => Discorb::Snowflake}] The ID mapping.
24
+ attr_reader :id_map
25
+
26
+ @types = {
27
+ 1 => :chat_input,
28
+ 2 => :user,
29
+ 3 => :message,
30
+ }.freeze
31
+
32
+ # @private
33
+ def initialize(name, guild_ids, block, type)
34
+ @name = name
35
+ @guild_ids = guild_ids&.map(&:to_s)
36
+ @block = block
37
+ @raw_type = type
38
+ @type = Discorb::ApplicationCommand::Command.types[type]
39
+ @type_raw = type
40
+ @id_map = Discorb::Dictionary.new
41
+ end
42
+
43
+ # @private
44
+ def replace_block(instance)
45
+ current_block = @block.dup
46
+ @block = proc do |*args|
47
+ instance.instance_exec(*args, &current_block)
48
+ end
49
+ end
50
+
51
+ # @private
52
+ def to_hash
53
+ {
54
+ name: @name,
55
+ default_permission: @default_permission,
56
+ type: @type_raw,
57
+ }
58
+ end
59
+
60
+ #
61
+ # Represents the slash command.
62
+ #
63
+ class SlashCommand < Command
64
+ # @return [String] The description of the command.
65
+ attr_reader :description
66
+ # @return [Hash{String => Hash}] The options of the command.
67
+ attr_reader :options
68
+
69
+ # @private
70
+ def initialize(name, description, options, guild_ids, block, type, parent)
71
+ super(name, guild_ids, block, type)
72
+ @description = description
73
+ @options = options
74
+ @id = nil
75
+ @parent = parent
76
+ @id_map = Discorb::Dictionary.new
77
+ end
78
+
79
+ #
80
+ # Returns the commands name.
81
+ #
82
+ # @return [String] The name of the command.
83
+ #
84
+ def to_s
85
+ (@parent + " " + @name).strip
86
+ end
87
+
88
+ # @private
89
+ def to_hash
90
+ options_payload = options.map do |name, value|
91
+ ret = {
92
+ type: case value[:type]
93
+ when String, :string, :str
94
+ 3
95
+ when Integer, :integer, :int
96
+ 4
97
+ when TrueClass, FalseClass, :boolean, :bool
98
+ 5
99
+ when Discorb::User, Discorb::Member, :user, :member
100
+ 6
101
+ when Discorb::Channel, :channel
102
+ 7
103
+ when Discorb::Role, :role
104
+ 8
105
+ when :mentionable
106
+ 9
107
+ when Float, :float
108
+ 10
109
+ when :attachment
110
+ 11
111
+ else
112
+ raise ArgumentError, "Invalid option type: #{value[:type]}"
113
+ end,
114
+ name: name,
115
+ description: value[:description],
116
+ required: value[:required].nil? ? !value[:optional] : value[:required],
117
+ }
118
+
119
+ ret[:choices] = value[:choices].map { |t| { name: t[0], value: t[1] } } if value[:choices]
120
+
121
+ ret[:channel_types] = value[:channel_types].map(&:channel_type) if value[:channel_types]
122
+
123
+ ret[:autocomplete] = !value[:autocomplete].nil? if value[:autocomplete]
124
+ if value[:range]
125
+ ret[:min_value] = value[:range].begin
126
+ ret[:max_value] = value[:range].end
127
+ end
128
+ ret
129
+ end
130
+ {
131
+ name: @name,
132
+ default_permission: true,
133
+ description: @description,
134
+ options: options_payload,
135
+ }
136
+ end
137
+ end
138
+
139
+ #
140
+ # Represents the command with subcommands.
141
+ #
142
+ class GroupCommand < Command
143
+ # @return [Array<Discorb::ApplicationCommand::Command::SlashCommand, Discorb::ApplicationCommand::Command::SubcommandGroup>] The subcommands of the command.
144
+ attr_reader :commands
145
+ # @return [String] The description of the command.
146
+ attr_reader :description
147
+
148
+ # @private
149
+ def initialize(name, description, guild_ids, type, client)
150
+ super(name, guild_ids, block, type)
151
+ @description = description
152
+ @commands = []
153
+ @client = client
154
+ @id_map = Discorb::Dictionary.new
155
+ end
156
+
157
+ #
158
+ # Add new subcommand.
159
+ #
160
+ # @param (see Discorb::ApplicationCommand::Handler#slash)
161
+ # @return [Discorb::ApplicationCommand::Command::SlashCommand] The added subcommand.
162
+ #
163
+ def slash(command_name, description, options = {}, &block)
164
+ command = Discorb::ApplicationCommand::Command::SlashCommand.new(command_name, description, options, [], block, 1, @name)
165
+ @client.bottom_commands << command
166
+ @commands << command
167
+ command
168
+ end
169
+
170
+ #
171
+ # Add new subcommand group.
172
+ #
173
+ # @param [String] command_name Group name.
174
+ # @param [String] description Group description.
175
+ #
176
+ # @yield Block to yield with the command.
177
+ # @yieldparam [Discorb::ApplicationCommand::Command::SubcommandGroup] group Group command.
178
+ #
179
+ # @return [Discorb::ApplicationCommand::Command::SubcommandGroup] Command object.
180
+ #
181
+ # @see file:docs/application_command.md Application Commands
182
+ #
183
+ def group(command_name, description, &block)
184
+ command = Discorb::ApplicationCommand::Command::SubcommandGroup.new(command_name, description, @name, @client)
185
+ command.yield_self(&block) if block_given?
186
+ @commands << command
187
+ command
188
+ end
189
+
190
+ #
191
+ # Returns the command name.
192
+ #
193
+ # @return [String] The command name.
194
+ #
195
+ def to_s
196
+ @name
197
+ end
198
+
199
+ # @private
200
+ def block_replace(instance)
201
+ super
202
+ @commands.each { |c| c.replace_block(instance) }
203
+ end
204
+
205
+ # @private
206
+ def to_hash
207
+ options_payload = @commands.map do |command|
208
+ if command.is_a?(SlashCommand)
209
+ {
210
+ name: command.name,
211
+ description: command.description,
212
+ default_permission: true,
213
+ type: 1,
214
+ options: command.to_hash[:options],
215
+ }
216
+ else
217
+ {
218
+ name: command.name,
219
+ description: command.description,
220
+ default_permission: true,
221
+ type: 2,
222
+ options: command.commands.map { |c| c.to_hash.merge(type: 1) },
223
+ }
224
+ end
225
+ end
226
+
227
+ {
228
+ name: @name,
229
+ default_permission: @enabled,
230
+ description: @description,
231
+ options: options_payload,
232
+ }
233
+ end
234
+ end
235
+
236
+ #
237
+ # Represents the subcommand group.
238
+ #
239
+ class SubcommandGroup < GroupCommand
240
+ # @return [Array<Discorb::ApplicationCommand::Command::SlashCommand>] The subcommands of the command.
241
+ attr_reader :commands
242
+
243
+ # @private
244
+ def initialize(name, description, parent, client)
245
+ super(name, description, [], 1, client)
246
+
247
+ @commands = []
248
+ @parent = parent
249
+ end
250
+
251
+ def to_s
252
+ @parent + " " + @name
253
+ end
254
+
255
+ #
256
+ # Add new subcommand.
257
+ # @param (see Discorb::ApplicationCommand::Handler#slash)
258
+ # @return [Discorb::ApplicationCommand::Command::SlashCommand] The added subcommand.
259
+ #
260
+ def slash(command_name, description, options = {}, &block)
261
+ command = Discorb::ApplicationCommand::Command::SlashCommand.new(command_name, description, options, [], block, 1, @parent + " " + @name)
262
+ @commands << command
263
+ @client.bottom_commands << command
264
+ command
265
+ end
266
+ end
267
+
268
+ class << self
269
+ # @private
270
+ attr_reader :types
271
+ end
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,168 @@
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
+ unless global_commands.empty?
133
+ @http.request(
134
+ Route.new(
135
+ "/applications/#{app_info.id}/commands",
136
+ "//applications/:application_id/commands",
137
+ :put
138
+ ),
139
+ global_commands.map(&:to_hash)
140
+ ).wait
141
+ end
142
+ if ENV["DISCORB_CLI_FLAG"] == "setup"
143
+ sputs "Registered commands for global:"
144
+ global_commands.each do |command|
145
+ iputs "- #{command.name}"
146
+ end
147
+ end
148
+ unless final_guild_ids.empty?
149
+ final_guild_ids.each do |guild_id|
150
+ commands = local_commands.select { |c| c.guild_ids.include?(guild_id) }
151
+ @http.request(
152
+ Route.new("/applications/#{app_info.id}/guilds/#{guild_id}/commands",
153
+ "//applications/:application_id/guilds/:guild_id/commands",
154
+ :put),
155
+ commands.map(&:to_hash)
156
+ ).wait
157
+ sputs "Registered commands for #{guild_id}:"
158
+ commands.each do |command|
159
+ iputs "- #{command.name}"
160
+ end
161
+ end
162
+ end
163
+ @log.info "Successfully setup commands"
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end