rubycord 1.0.0

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