discordrb 3.4.3 → 3.5.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +44 -18
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
  5. data/.github/workflows/codeql.yml +65 -0
  6. data/.markdownlint.json +4 -0
  7. data/.rubocop.yml +8 -2
  8. data/CHANGELOG.md +390 -225
  9. data/LICENSE.txt +1 -1
  10. data/README.md +37 -25
  11. data/discordrb-webhooks.gemspec +4 -1
  12. data/discordrb.gemspec +9 -6
  13. data/lib/discordrb/api/application.rb +202 -0
  14. data/lib/discordrb/api/channel.rb +177 -11
  15. data/lib/discordrb/api/interaction.rb +54 -0
  16. data/lib/discordrb/api/invite.rb +2 -2
  17. data/lib/discordrb/api/server.rb +40 -19
  18. data/lib/discordrb/api/user.rb +8 -3
  19. data/lib/discordrb/api/webhook.rb +57 -0
  20. data/lib/discordrb/api.rb +19 -5
  21. data/lib/discordrb/bot.rb +317 -32
  22. data/lib/discordrb/cache.rb +27 -22
  23. data/lib/discordrb/commands/command_bot.rb +6 -4
  24. data/lib/discordrb/commands/container.rb +1 -1
  25. data/lib/discordrb/commands/parser.rb +2 -2
  26. data/lib/discordrb/commands/rate_limiter.rb +1 -1
  27. data/lib/discordrb/container.rb +132 -3
  28. data/lib/discordrb/data/attachment.rb +15 -0
  29. data/lib/discordrb/data/audit_logs.rb +3 -3
  30. data/lib/discordrb/data/channel.rb +167 -23
  31. data/lib/discordrb/data/component.rb +229 -0
  32. data/lib/discordrb/data/integration.rb +42 -3
  33. data/lib/discordrb/data/interaction.rb +800 -0
  34. data/lib/discordrb/data/invite.rb +1 -1
  35. data/lib/discordrb/data/member.rb +108 -33
  36. data/lib/discordrb/data/message.rb +99 -19
  37. data/lib/discordrb/data/overwrite.rb +13 -7
  38. data/lib/discordrb/data/role.rb +58 -1
  39. data/lib/discordrb/data/server.rb +82 -80
  40. data/lib/discordrb/data/user.rb +69 -9
  41. data/lib/discordrb/data/webhook.rb +97 -4
  42. data/lib/discordrb/data.rb +3 -0
  43. data/lib/discordrb/errors.rb +44 -3
  44. data/lib/discordrb/events/channels.rb +1 -1
  45. data/lib/discordrb/events/interactions.rb +482 -0
  46. data/lib/discordrb/events/message.rb +9 -6
  47. data/lib/discordrb/events/presence.rb +21 -14
  48. data/lib/discordrb/events/reactions.rb +0 -1
  49. data/lib/discordrb/events/threads.rb +96 -0
  50. data/lib/discordrb/gateway.rb +30 -17
  51. data/lib/discordrb/permissions.rb +59 -34
  52. data/lib/discordrb/version.rb +1 -1
  53. data/lib/discordrb/voice/encoder.rb +2 -2
  54. data/lib/discordrb/voice/network.rb +18 -7
  55. data/lib/discordrb/voice/sodium.rb +3 -1
  56. data/lib/discordrb/voice/voice_bot.rb +3 -3
  57. data/lib/discordrb/webhooks.rb +2 -0
  58. data/lib/discordrb.rb +37 -4
  59. metadata +48 -14
  60. data/.codeclimate.yml +0 -16
  61. data/.travis.yml +0 -32
  62. data/bin/travis_build_docs.sh +0 -17
@@ -0,0 +1,800 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'discordrb/webhooks'
4
+
5
+ module Discordrb
6
+ # Base class for interaction objects.
7
+ class Interaction
8
+ # Interaction types.
9
+ # @see https://discord.com/developers/docs/interactions/slash-commands#interaction-interactiontype
10
+ TYPES = {
11
+ ping: 1,
12
+ command: 2,
13
+ component: 3,
14
+ modal_submit: 5
15
+ }.freeze
16
+
17
+ # Interaction response types.
18
+ # @see https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactioncallbacktype
19
+ CALLBACK_TYPES = {
20
+ pong: 1,
21
+ channel_message: 4,
22
+ deferred_message: 5,
23
+ deferred_update: 6,
24
+ update_message: 7,
25
+ modal: 9
26
+ }.freeze
27
+
28
+ # @return [User, Member] The user that initiated the interaction.
29
+ attr_reader :user
30
+
31
+ # @return [Integer, nil] The ID of the server this interaction originates from.
32
+ attr_reader :server_id
33
+
34
+ # @return [Integer] The ID of the channel this interaction originates from.
35
+ attr_reader :channel_id
36
+
37
+ # @return [Integer] The ID of this interaction.
38
+ attr_reader :id
39
+
40
+ # @return [Integer] The ID of the application associated with this interaction.
41
+ attr_reader :application_id
42
+
43
+ # @return [String] The interaction token.
44
+ attr_reader :token
45
+
46
+ # @!visibility private
47
+ # @return [Integer] Currently pointless
48
+ attr_reader :version
49
+
50
+ # @return [Integer] The type of this interaction.
51
+ # @see TYPES
52
+ attr_reader :type
53
+
54
+ # @return [Hash] The interaction data.
55
+ attr_reader :data
56
+
57
+ # @return [Array<ActionRow>]
58
+ attr_reader :components
59
+
60
+ # @!visibility private
61
+ def initialize(data, bot)
62
+ @bot = bot
63
+
64
+ @id = data['id'].to_i
65
+ @application_id = data['application_id'].to_i
66
+ @type = data['type']
67
+ @message = data['message']
68
+ @data = data['data']
69
+ @server_id = data['guild_id']&.to_i
70
+ @channel_id = data['channel_id']&.to_i
71
+ @user = if data['member']
72
+ data['member']['guild_id'] = @server_id
73
+ Discordrb::Member.new(data['member'], bot.servers[@server_id], bot)
74
+ else
75
+ bot.ensure_user(data['user'])
76
+ end
77
+ @token = data['token']
78
+ @version = data['version']
79
+ @components = @data['components']&.map { |component| Components.from_data(component, @bot) }&.compact || []
80
+ end
81
+
82
+ # Respond to the creation of this interaction. An interaction must be responded to or deferred,
83
+ # The response may be modified with {Interaction#edit_response} or deleted with {Interaction#delete_response}.
84
+ # Further messages can be sent with {Interaction#send_message}.
85
+ # @param content [String] The content of the message.
86
+ # @param tts [true, false]
87
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
88
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
89
+ # @param flags [Integer] Message flags.
90
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
91
+ # @param wait [true, false] Whether this method should return a Message object of the interaction response.
92
+ # @param components [Array<#to_h>] An array of components
93
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
94
+ # @yieldparam view [Webhooks::View] A builder for creating interaction components.
95
+ def respond(content: nil, tts: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: nil, wait: false, components: nil)
96
+ flags |= 1 << 6 if ephemeral
97
+
98
+ builder = Discordrb::Webhooks::Builder.new
99
+ view = Discordrb::Webhooks::View.new
100
+
101
+ # Set builder defaults from parameters
102
+ prepare_builder(builder, content, embeds, allowed_mentions)
103
+ yield(builder, view) if block_given?
104
+
105
+ components ||= view
106
+ data = builder.to_json_hash
107
+
108
+ Discordrb::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:channel_message], data[:content], tts, data[:embeds], data[:allowed_mentions], flags, components.to_a)
109
+
110
+ return unless wait
111
+
112
+ response = Discordrb::API::Interaction.get_original_interaction_response(@token, @application_id)
113
+ Interactions::Message.new(JSON.parse(response), @bot, @interaction)
114
+ end
115
+
116
+ # Defer an interaction, setting a temporary response that can be later overriden by {Interaction#send_message}.
117
+ # This method is used when you want to use a single message for your response but require additional processing time, or to simply ack
118
+ # an interaction so an error is not displayed.
119
+ # @param flags [Integer] Message flags.
120
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
121
+ def defer(flags: 0, ephemeral: true)
122
+ flags |= 1 << 6 if ephemeral
123
+
124
+ Discordrb::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:deferred_message], nil, nil, nil, nil, flags)
125
+ nil
126
+ end
127
+
128
+ # Defer an update to an interaction. This is can only currently used by Button interactions.
129
+ def defer_update
130
+ Discordrb::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:deferred_update])
131
+ end
132
+
133
+ # Create a modal as a response.
134
+ # @param title [String] The title of the modal being shown.
135
+ # @param custom_id [String] The custom_id used to identify the modal and store data.
136
+ # @param components [Array<Component, Hash>, nil] An array of components. These can be defined through the block as well.
137
+ # @yieldparam [Discordrb::Webhooks::Modal] A builder for the modal's components.
138
+ def show_modal(title:, custom_id:, components: nil)
139
+ if block_given?
140
+ modal_builder = Discordrb::Webhooks::Modal.new
141
+ yield modal_builder
142
+
143
+ components = modal_builder.to_a
144
+ end
145
+
146
+ Discordrb::API::Interaction.create_interaction_modal_response(@token, @id, custom_id, title, components.to_a) unless type == Interaction::TYPES[:modal_submit]
147
+ nil
148
+ end
149
+
150
+ # Respond to the creation of this interaction. An interaction must be responded to or deferred,
151
+ # The response may be modified with {Interaction#edit_response} or deleted with {Interaction#delete_response}.
152
+ # Further messages can be sent with {Interaction#send_message}.
153
+ # @param content [String] The content of the message.
154
+ # @param tts [true, false]
155
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
156
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
157
+ # @param flags [Integer] Message flags.
158
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
159
+ # @param wait [true, false] Whether this method should return a Message object of the interaction response.
160
+ # @param components [Array<#to_h>] An array of components
161
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
162
+ # @yieldparam view [Webhooks::View] A builder for creating interaction components.
163
+ def update_message(content: nil, tts: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: nil, wait: false, components: nil)
164
+ flags |= 1 << 6 if ephemeral
165
+
166
+ builder = Discordrb::Webhooks::Builder.new
167
+ view = Discordrb::Webhooks::View.new
168
+
169
+ prepare_builder(builder, content, embeds, allowed_mentions)
170
+ yield(builder, view) if block_given?
171
+
172
+ components ||= view
173
+ data = builder.to_json_hash
174
+
175
+ Discordrb::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:update_message], data[:content], tts, data[:embeds], data[:allowed_mentions], flags, components.to_a)
176
+
177
+ return unless wait
178
+
179
+ response = Discordrb::API::Interaction.get_original_interaction_response(@token, @application_id)
180
+ Interactions::Message.new(JSON.parse(response), @bot, @interaction)
181
+ end
182
+
183
+ # Edit the original response to this interaction.
184
+ # @param content [String] The content of the message.
185
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
186
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
187
+ # @param components [Array<#to_h>] An array of components
188
+ # @return [InteractionMessage] The updated response message.
189
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
190
+ def edit_response(content: nil, embeds: nil, allowed_mentions: nil, components: nil)
191
+ builder = Discordrb::Webhooks::Builder.new
192
+ view = Discordrb::Webhooks::View.new
193
+
194
+ prepare_builder(builder, content, embeds, allowed_mentions)
195
+ yield(builder, view) if block_given?
196
+
197
+ components ||= view
198
+ data = builder.to_json_hash
199
+ resp = Discordrb::API::Interaction.edit_original_interaction_response(@token, @application_id, data[:content], data[:embeds], data[:allowed_mentions], components.to_a)
200
+
201
+ Interactions::Message.new(JSON.parse(resp), @bot, @interaction)
202
+ end
203
+
204
+ # Delete the original interaction response.
205
+ def delete_response
206
+ Discordrb::API::Interaction.delete_original_interaction_response(@token, @application_id)
207
+ end
208
+
209
+ # @param content [String] The content of the message.
210
+ # @param tts [true, false]
211
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
212
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
213
+ # @param flags [Integer] Message flags.
214
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
215
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
216
+ def send_message(content: nil, embeds: nil, tts: false, allowed_mentions: nil, flags: 0, ephemeral: false, components: nil)
217
+ flags |= 64 if ephemeral
218
+
219
+ builder = Discordrb::Webhooks::Builder.new
220
+ view = Discordrb::Webhooks::View.new
221
+
222
+ prepare_builder(builder, content, embeds, allowed_mentions)
223
+ yield builder, view if block_given?
224
+
225
+ components ||= view
226
+ data = builder.to_json_hash
227
+
228
+ resp = Discordrb::API::Webhook.token_execute_webhook(
229
+ @token, @application_id, true, data[:content], nil, nil, tts, nil, data[:embeds], data[:allowed_mentions], flags, components.to_a
230
+ )
231
+ Interactions::Message.new(JSON.parse(resp), @bot, @interaction)
232
+ end
233
+
234
+ # @param message [String, Integer, InteractionMessage, Message] The message created by this interaction to be edited.
235
+ # @param content [String] The message content.
236
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
237
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
238
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
239
+ def edit_message(message, content: nil, embeds: nil, allowed_mentions: nil, components: nil)
240
+ builder = Discordrb::Webhooks::Builder.new
241
+ view = Discordrb::Webhooks::View.new
242
+
243
+ prepare_builder(builder, content, embeds, allowed_mentions)
244
+ yield builder, view if block_given?
245
+
246
+ components ||= view
247
+ data = builder.to_json_hash
248
+
249
+ resp = Discordrb::API::Webhook.token_edit_message(
250
+ @token, @application_id, message.resolve_id, data[:content], data[:embeds], data[:allowed_mentions], components.to_a
251
+ )
252
+ Interactions::Message.new(JSON.parse(resp), @bot, @interaction)
253
+ end
254
+
255
+ # @param message [Integer, String, InteractionMessage, Message] The message created by this interaction to be deleted.
256
+ def delete_message(message)
257
+ Discordrb::API::Webhook.token_delete_message(@token, @application_id, message.resolve_id)
258
+ nil
259
+ end
260
+
261
+ # @return [Server, nil] This will be nil for interactions that occur in DM channels or servers where the bot
262
+ # does not have the `bot` scope.
263
+ def server
264
+ @bot.server(@server_id)
265
+ end
266
+
267
+ # @return [Channel, nil]
268
+ # @raise [Errors::NoPermission] When the bot is not in the server associated with this interaction.
269
+ def channel
270
+ @bot.channel(@channel_id)
271
+ end
272
+
273
+ # @return [Hash, nil] Returns the button that triggered this interaction if applicable, otherwise nil
274
+ def button
275
+ return unless @type == TYPES[:component]
276
+
277
+ @message['components'].each do |row|
278
+ Components::ActionRow.new(row, @bot).buttons.each do |button|
279
+ return button if button.custom_id == @data['custom_id']
280
+ end
281
+ end
282
+ end
283
+
284
+ # @return [Array<TextInput>]
285
+ def text_inputs
286
+ @components&.select { |component| component.is_a? TextInput } | []
287
+ end
288
+
289
+ # @return [TextInput, Button, SelectMenu]
290
+ def get_component(custom_id)
291
+ top_level = @components.flat_map(&:components) || []
292
+ message_level = (@message.instance_of?(Hash) ? Message.new(@message, @bot) : @message)&.components&.flat_map(&:components) || []
293
+ components = top_level.concat(message_level)
294
+ components.find { |component| component.custom_id == custom_id }
295
+ end
296
+
297
+ private
298
+
299
+ # Set builder defaults from parameters
300
+ # @param builder [Discordrb::Webhooks::Builder]
301
+ # @param content [String, nil]
302
+ # @param embeds [Array<Hash, Discordrb::Webhooks::Embed>, nil]
303
+ # @param allowed_mentions [AllowedMentions, Hash, nil]
304
+ def prepare_builder(builder, content, embeds, allowed_mentions)
305
+ builder.content = content
306
+ builder.allowed_mentions = allowed_mentions
307
+ embeds&.each { |embed| builder << embed }
308
+ end
309
+ end
310
+
311
+ # An ApplicationCommand for slash commands.
312
+ class ApplicationCommand
313
+ # Command types. `chat_input` is a command that appears in the text input field. `user` and `message` types appear as context menus
314
+ # for the respective resource.
315
+ TYPES = {
316
+ chat_input: 1,
317
+ user: 2,
318
+ message: 3
319
+ }.freeze
320
+
321
+ # @return [Integer]
322
+ attr_reader :application_id
323
+
324
+ # @return [Integer, nil]
325
+ attr_reader :server_id
326
+
327
+ # @return [String]
328
+ attr_reader :name
329
+
330
+ # @return [String]
331
+ attr_reader :description
332
+
333
+ # @return [true, false]
334
+ attr_reader :default_permission
335
+
336
+ # @return [Hash]
337
+ attr_reader :options
338
+
339
+ # @return [Integer]
340
+ attr_reader :id
341
+
342
+ # @!visibility private
343
+ def initialize(data, bot, server_id = nil)
344
+ @bot = bot
345
+ @id = data['id'].to_i
346
+ @application_id = data['application_id'].to_i
347
+ @server_id = server_id&.to_i
348
+
349
+ @name = data['name']
350
+ @description = data['description']
351
+ @default_permission = data['default_permission']
352
+ @options = data['options']
353
+ end
354
+
355
+ # @param subcommand [String, nil] The subcommand to mention.
356
+ # @param subcommand_group [String, nil] The subcommand group to mention.
357
+ # @return [String] the layout to mention it in a message
358
+ def mention(subcommand_group: nil, subcommand: nil)
359
+ if subcommand_group && subcommand
360
+ "</#{name} #{subcommand_group} #{subcommand}:#{id}>"
361
+ elsif subcommand_group
362
+ "</#{name} #{subcommand_group}:#{id}>"
363
+ elsif subcommand
364
+ "</#{name} #{subcommand}:#{id}>"
365
+ else
366
+ "</#{name}:#{id}>"
367
+ end
368
+ end
369
+
370
+ alias_method :to_s, :mention
371
+
372
+ # @param name [String] The name to use for this command.
373
+ # @param description [String] The description of this command.
374
+ # @param default_permission [true, false] Whether this command is available with default permissions.
375
+ # @yieldparam (see Bot#edit_application_command)
376
+ # @return (see Bot#edit_application_command)
377
+ def edit(name: nil, description: nil, default_permission: nil, &block)
378
+ @bot.edit_application_command(@id, server_id: @server_id, name: name, description: description, default_permission: default_permission, &block)
379
+ end
380
+
381
+ # Delete this application command.
382
+ # @return (see Bot#delete_application_command)
383
+ def delete
384
+ @bot.delete_application_command(@id, server_id: @server_id)
385
+ end
386
+ end
387
+
388
+ # Objects specific to Interactions.
389
+ module Interactions
390
+ # A builder for defining slash commands options.
391
+ class OptionBuilder
392
+ # @!visibility private
393
+ TYPES = {
394
+ subcommand: 1,
395
+ subcommand_group: 2,
396
+ string: 3,
397
+ integer: 4,
398
+ boolean: 5,
399
+ user: 6,
400
+ channel: 7,
401
+ role: 8,
402
+ mentionable: 9,
403
+ number: 10,
404
+ attachment: 11
405
+ }.freeze
406
+
407
+ # Channel types that can be provided to #channel
408
+ CHANNEL_TYPES = {
409
+ text: 0,
410
+ dm: 1,
411
+ voice: 2,
412
+ group_dm: 3,
413
+ category: 4,
414
+ news: 5,
415
+ store: 6,
416
+ news_thread: 10,
417
+ public_thread: 11,
418
+ private_thread: 12,
419
+ stage: 13
420
+ }.freeze
421
+
422
+ # @return [Array<Hash>]
423
+ attr_reader :options
424
+
425
+ # @!visibility private
426
+ def initialize
427
+ @options = []
428
+ end
429
+
430
+ # @param name [String, Symbol] The name of the subcommand.
431
+ # @param description [String] A description of the subcommand.
432
+ # @yieldparam [OptionBuilder]
433
+ # @return (see #option)
434
+ # @example
435
+ # bot.register_application_command(:test, 'Test command') do |cmd|
436
+ # cmd.subcommand(:echo) do |sub|
437
+ # sub.string('message', 'What to echo back', required: true)
438
+ # end
439
+ # end
440
+ def subcommand(name, description)
441
+ builder = OptionBuilder.new
442
+ yield builder if block_given?
443
+
444
+ option(TYPES[:subcommand], name, description, options: builder.to_a)
445
+ end
446
+
447
+ # @param name [String, Symbol] The name of the subcommand group.
448
+ # @param description [String] A description of the subcommand group.
449
+ # @yieldparam [OptionBuilder]
450
+ # @return (see #option)
451
+ # @example
452
+ # bot.register_application_command(:test, 'Test command') do |cmd|
453
+ # cmd.subcommand_group(:fun) do |group|
454
+ # group.subcommand(:8ball) do |sub|
455
+ # sub.string(:question, 'What do you ask the mighty 8ball?')
456
+ # end
457
+ # end
458
+ # end
459
+ def subcommand_group(name, description)
460
+ builder = OptionBuilder.new
461
+ yield builder
462
+
463
+ option(TYPES[:subcommand_group], name, description, options: builder.to_a)
464
+ end
465
+
466
+ # @param name [String, Symbol] The name of the argument.
467
+ # @param description [String] A description of the argument.
468
+ # @param required [true, false] Whether this option must be provided.
469
+ # @param choices [Hash, nil] Available choices, mapped as `Name => Value`.
470
+ # @return (see #option)
471
+ def string(name, description, required: nil, choices: nil)
472
+ option(TYPES[:string], name, description, required: required, choices: choices)
473
+ end
474
+
475
+ # @param name [String, Symbol] The name of the argument.
476
+ # @param description [String] A description of the argument.
477
+ # @param required [true, false] Whether this option must be provided.
478
+ # @param choices [Hash, nil] Available choices, mapped as `Name => Value`.
479
+ # @return (see #option)
480
+ def integer(name, description, required: nil, choices: nil)
481
+ option(TYPES[:integer], name, description, required: required, choices: choices)
482
+ end
483
+
484
+ # @param name [String, Symbol] The name of the argument.
485
+ # @param description [String] A description of the argument.
486
+ # @param required [true, false] Whether this option must be provided.
487
+ # @return (see #option)
488
+ def boolean(name, description, required: nil)
489
+ option(TYPES[:boolean], name, description, required: required)
490
+ end
491
+
492
+ # @param name [String, Symbol] The name of the argument.
493
+ # @param description [String] A description of the argument.
494
+ # @param required [true, false] Whether this option must be provided.
495
+ # @return (see #option)
496
+ def user(name, description, required: nil)
497
+ option(TYPES[:user], name, description, required: required)
498
+ end
499
+
500
+ # @param name [String, Symbol] The name of the argument.
501
+ # @param description [String] A description of the argument.
502
+ # @param required [true, false] Whether this option must be provided.
503
+ # @param types [Array<Symbol, Integer>] See {CHANNEL_TYPES}
504
+ # @return (see #option)
505
+ def channel(name, description, required: nil, types: nil)
506
+ types = types&.collect { |type| type.is_a?(Numeric) ? type : CHANNEL_TYPES[type] }
507
+ option(TYPES[:channel], name, description, required: required, channel_types: types)
508
+ end
509
+
510
+ # @param name [String, Symbol] The name of the argument.
511
+ # @param description [String] A description of the argument.
512
+ # @param required [true, false] Whether this option must be provided.
513
+ # @return (see #option)
514
+ def role(name, description, required: nil)
515
+ option(TYPES[:role], name, description, required: required)
516
+ end
517
+
518
+ # @param name [String, Symbol] The name of the argument.
519
+ # @param description [String] A description of the argument.
520
+ # @param required [true, false] Whether this option must be provided.
521
+ # @return (see #option)
522
+ def mentionable(name, description, required: nil)
523
+ option(TYPES[:mentionable], name, description, required: required)
524
+ end
525
+
526
+ # @param name [String, Symbol] The name of the argument.
527
+ # @param description [String] A description of the argument.
528
+ # @param required [true, false] Whether this option must be provided.
529
+ # @return (see #option)
530
+ def number(name, description, required: nil, min_value: nil, max_value: nil, choices: nil)
531
+ option(TYPES[:number], name, description,
532
+ required: required, min_value: min_value, max_value: max_value, choices: choices)
533
+ end
534
+
535
+ # @param name [String, Symbol] The name of the argument.
536
+ # @param description [String] A description of the argument.
537
+ # @param required [true, false] Whether this option must be provided.
538
+ # @return (see #option)
539
+ def attachment(name, description, required: nil)
540
+ option(TYPES[:attachment], name, description, required: required)
541
+ end
542
+
543
+ # @!visibility private
544
+ # @param type [Integer] The argument type.
545
+ # @param name [String, Symbol] The name of the argument.
546
+ # @param description [String] A description of the argument.
547
+ # @param required [true, false] Whether this option must be provided.
548
+ # @param min_value [Integer, Float] A minimum value for integer and number options.
549
+ # @param max_value [Integer, Float] A maximum value for integer and number options.
550
+ # @param channel_types [Array<Integer>] Channel types that can be provides for channel options.
551
+ # @return Hash
552
+ def option(type, name, description, required: nil, choices: nil, options: nil, min_value: nil, max_value: nil,
553
+ channel_types: nil)
554
+ opt = { type: type, name: name, description: description }
555
+ choices = choices.map { |option_name, value| { name: option_name, value: value } } if choices
556
+
557
+ opt.merge!({ required: required, choices: choices, options: options, min_value: min_value,
558
+ max_value: max_value, channel_types: channel_types }.compact)
559
+
560
+ @options << opt
561
+ opt
562
+ end
563
+
564
+ # @return [Array<Hash>]
565
+ def to_a
566
+ @options
567
+ end
568
+ end
569
+
570
+ # Builder for creating server application command permissions.
571
+ # @deprecated This system is being replaced in the near future.
572
+ class PermissionBuilder
573
+ # Role permission type
574
+ ROLE = 1
575
+ # User permission type
576
+ USER = 2
577
+
578
+ # @!visibility hidden
579
+ def initialize
580
+ @permissions = []
581
+ end
582
+
583
+ # Allow a role to use this command.
584
+ # @param role_id [Integer]
585
+ # @return [PermissionBuilder]
586
+ def allow_role(role_id)
587
+ create_entry(role_id, ROLE, true)
588
+ end
589
+
590
+ # Deny a role usage of this command.
591
+ # @param role_id [Integer]
592
+ # @return [PermissionBuilder]
593
+ def deny_role(role_id)
594
+ create_entry(role_id, ROLE, false)
595
+ end
596
+
597
+ # Allow a user to use this command.
598
+ # @param user_id [Integer]
599
+ # @return [PermissionBuilder]
600
+ def allow_user(user_id)
601
+ create_entry(user_id, USER, true)
602
+ end
603
+
604
+ # Deny a user usage of this command.
605
+ # @param user_id [Integer]
606
+ # @return [PermissionBuilder]
607
+ def deny_user(user_id)
608
+ create_entry(user_id, USER, false)
609
+ end
610
+
611
+ # Allow an entity to use this command.
612
+ # @param object [Role, User, Member]
613
+ # @return [PermissionBuilder]
614
+ # @raise [ArgumentError]
615
+ def allow(object)
616
+ case object
617
+ when Discordrb::User, Discordrb::Member
618
+ create_entry(object.id, USER, true)
619
+ when Discordrb::Role
620
+ create_entry(object.id, ROLE, true)
621
+ else
622
+ raise ArgumentError, "Unable to create permission for unknown type: #{object.class}"
623
+ end
624
+ end
625
+
626
+ # Deny an entity usage of this command.
627
+ # @param object [Role, User, Member]
628
+ # @return [PermissionBuilder]
629
+ # @raise [ArgumentError]
630
+ def deny(object)
631
+ case object
632
+ when Discordrb::User, Discordrb::Member
633
+ create_entry(object.id, USER, false)
634
+ when Discordrb::Role
635
+ create_entry(object.id, ROLE, false)
636
+ else
637
+ raise ArgumentError, "Unable to create permission for unknown type: #{object.class}"
638
+ end
639
+ end
640
+
641
+ # @!visibility private
642
+ # @return [Array<Hash>]
643
+ def to_a
644
+ @permissions
645
+ end
646
+
647
+ private
648
+
649
+ def create_entry(id, type, permission)
650
+ @permissions << { id: id, type: type, permission: permission }
651
+ self
652
+ end
653
+ end
654
+
655
+ # A message partial for interactions.
656
+ class Message
657
+ include IDObject
658
+
659
+ # @return [Interaction] The interaction that created this message.
660
+ attr_reader :interaction
661
+
662
+ # @return [String, nil] The content of the message.
663
+ attr_reader :content
664
+
665
+ # @return [true, false] Whether this message is pinned in the channel it belongs to.
666
+ attr_reader :pinned
667
+
668
+ # @return [true, false]
669
+ attr_reader :tts
670
+
671
+ # @return [Time]
672
+ attr_reader :timestamp
673
+
674
+ # @return [Time, nil]
675
+ attr_reader :edited_timestamp
676
+
677
+ # @return [true, false]
678
+ attr_reader :edited
679
+
680
+ # @return [Integer]
681
+ attr_reader :id
682
+
683
+ # @return [User] The user of the application.
684
+ attr_reader :author
685
+
686
+ # @return [Attachment]
687
+ attr_reader :attachments
688
+
689
+ # @return [Array<Embed>]
690
+ attr_reader :embeds
691
+
692
+ # @return [Array<User>]
693
+ attr_reader :mentions
694
+
695
+ # @return [Integer]
696
+ attr_reader :flags
697
+
698
+ # @return [Integer]
699
+ attr_reader :channel_id
700
+
701
+ # @return [Hash, nil]
702
+ attr_reader :message_reference
703
+
704
+ # @return [Array<Component>]
705
+ attr_reader :components
706
+
707
+ # @!visibility private
708
+ def initialize(data, bot, interaction)
709
+ @data = data
710
+ @bot = bot
711
+ @interaction = interaction
712
+ @content = data['content']
713
+ @channel_id = data['channel_id'].to_i
714
+ @pinned = data['pinned']
715
+ @tts = data['tts']
716
+
717
+ @message_reference = data['message_reference']
718
+
719
+ @server_id = data['guild_id']&.to_i
720
+
721
+ @timestamp = Time.parse(data['timestamp']) if data['timestamp']
722
+ @edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
723
+ @edited = !@edited_timestamp.nil?
724
+
725
+ @id = data['id'].to_i
726
+
727
+ @author = bot.ensure_user(data['author'] || data['member']['user'])
728
+
729
+ @attachments = []
730
+ @attachments = data['attachments'].map { |e| Attachment.new(e, self, @bot) } if data['attachments']
731
+
732
+ @embeds = []
733
+ @embeds = data['embeds'].map { |e| Embed.new(e, self) } if data['embeds']
734
+
735
+ @mentions = []
736
+
737
+ data['mentions']&.each do |element|
738
+ @mentions << bot.ensure_user(element)
739
+ end
740
+
741
+ @mention_roles = data['mention_roles']
742
+ @mention_everyone = data['mention_everyone']
743
+ @flags = data['flags']
744
+ @pinned = data['pinned']
745
+ @components = data['components'].map { |component_data| Components.from_data(component_data, @bot) } if data['components']
746
+ end
747
+
748
+ # @return [Member, nil] This will return nil if the bot does not have access to the
749
+ # server the interaction originated in.
750
+ def member
751
+ server&.member(@user.id)
752
+ end
753
+
754
+ # @return [Server, nil] This will return nil if the bot does not have access to the
755
+ # server the interaction originated in.
756
+ def server
757
+ @bot.server(@server_id)
758
+ end
759
+
760
+ # @return [Channel] The channel the interaction originates from.
761
+ # @raise [Errors::NoPermission] When the bot is not in the server associated with this interaction.
762
+ def channel
763
+ @bot.channel(@channel_id)
764
+ end
765
+
766
+ # Respond to this message.
767
+ # @param (see Interaction#send_message)
768
+ # @yieldparam (see Interaction#send_message)
769
+ def respond(content: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: true, components: nil, &block)
770
+ @interaction.send_message(content: content, embeds: embeds, allowed_mentions: allowed_mentions, flags: flags, ephemeral: ephemeral, components: components, &block)
771
+ end
772
+
773
+ # Delete this message.
774
+ def delete
775
+ @interaction.delete_message(@id)
776
+ end
777
+
778
+ # Edit this message's data.
779
+ # @param content (see Interaction#send_message)
780
+ # @param embeds (see Interaction#send_message)
781
+ # @param allowed_mentions (see Interaction#send_message)
782
+ # @yieldparam (see Interaction#send_message)
783
+ def edit(content: nil, embeds: nil, allowed_mentions: nil, components: nil, &block)
784
+ @interaction.edit_message(@id, content: content, embeds: embeds, allowed_mentions: allowed_mentions, components: components, &block)
785
+ end
786
+
787
+ # @return [Discordrb::Message]
788
+ def to_message
789
+ Discordrb::Message.new(@data, @bot)
790
+ end
791
+
792
+ alias_method :message, :to_message
793
+
794
+ # @!visibility private
795
+ def inspect
796
+ "<Interaction::Message content=#{@content.inspect} embeds=#{@embeds.inspect} channel_id=#{@channel_id} server_id=#{@server_id} author=#{@author.inspect}>"
797
+ end
798
+ end
799
+ end
800
+ end