rubycord 1.0.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 (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