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
@@ -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