onyxcord 1.1.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 (133) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +13 -0
  3. data/.devcontainer/devcontainer.json +29 -0
  4. data/.devcontainer/postcreate.sh +4 -0
  5. data/.github/CONTRIBUTING.md +13 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. data/.github/pull_request_template.md +37 -0
  9. data/.github/workflows/ci.yml +78 -0
  10. data/.github/workflows/codeql.yml +65 -0
  11. data/.github/workflows/deploy.yml +54 -0
  12. data/.github/workflows/release.yml +51 -0
  13. data/.gitignore +16 -0
  14. data/.markdownlint.json +4 -0
  15. data/.overcommit.yml +7 -0
  16. data/.rspec +2 -0
  17. data/.rubocop.yml +129 -0
  18. data/.yardopts +1 -0
  19. data/CHANGELOG.md +0 -0
  20. data/Gemfile +7 -0
  21. data/LICENSE.txt +21 -0
  22. data/README.md +305 -0
  23. data/Rakefile +17 -0
  24. data/bin/console +15 -0
  25. data/bin/setup +7 -0
  26. data/lib/onyxcord/allowed_mentions.rb +43 -0
  27. data/lib/onyxcord/api/application.rb +316 -0
  28. data/lib/onyxcord/api/channel.rb +700 -0
  29. data/lib/onyxcord/api/interaction.rb +67 -0
  30. data/lib/onyxcord/api/invite.rb +44 -0
  31. data/lib/onyxcord/api/server.rb +775 -0
  32. data/lib/onyxcord/api/user.rb +158 -0
  33. data/lib/onyxcord/api/webhook.rb +163 -0
  34. data/lib/onyxcord/api.rb +335 -0
  35. data/lib/onyxcord/await.rb +51 -0
  36. data/lib/onyxcord/bot.rb +1971 -0
  37. data/lib/onyxcord/cache.rb +326 -0
  38. data/lib/onyxcord/colour_rgb.rb +43 -0
  39. data/lib/onyxcord/commands/command_bot.rb +511 -0
  40. data/lib/onyxcord/commands/container.rb +112 -0
  41. data/lib/onyxcord/commands/events.rb +11 -0
  42. data/lib/onyxcord/commands/parser.rb +327 -0
  43. data/lib/onyxcord/commands/rate_limiter.rb +144 -0
  44. data/lib/onyxcord/configuration.rb +125 -0
  45. data/lib/onyxcord/container.rb +988 -0
  46. data/lib/onyxcord/data/activity.rb +271 -0
  47. data/lib/onyxcord/data/application.rb +341 -0
  48. data/lib/onyxcord/data/attachment.rb +91 -0
  49. data/lib/onyxcord/data/audit_logs.rb +438 -0
  50. data/lib/onyxcord/data/avatar_decoration.rb +26 -0
  51. data/lib/onyxcord/data/call.rb +22 -0
  52. data/lib/onyxcord/data/channel.rb +1355 -0
  53. data/lib/onyxcord/data/channel_tag.rb +69 -0
  54. data/lib/onyxcord/data/collectibles.rb +47 -0
  55. data/lib/onyxcord/data/component.rb +583 -0
  56. data/lib/onyxcord/data/embed.rb +258 -0
  57. data/lib/onyxcord/data/emoji.rb +123 -0
  58. data/lib/onyxcord/data/install_params.rb +24 -0
  59. data/lib/onyxcord/data/integration.rb +144 -0
  60. data/lib/onyxcord/data/interaction.rb +1141 -0
  61. data/lib/onyxcord/data/invite.rb +137 -0
  62. data/lib/onyxcord/data/member.rb +528 -0
  63. data/lib/onyxcord/data/message.rb +612 -0
  64. data/lib/onyxcord/data/message_activity.rb +41 -0
  65. data/lib/onyxcord/data/overwrite.rb +109 -0
  66. data/lib/onyxcord/data/poll.rb +365 -0
  67. data/lib/onyxcord/data/primary_server.rb +60 -0
  68. data/lib/onyxcord/data/profile.rb +79 -0
  69. data/lib/onyxcord/data/reaction.rb +64 -0
  70. data/lib/onyxcord/data/recipient.rb +34 -0
  71. data/lib/onyxcord/data/role.rb +449 -0
  72. data/lib/onyxcord/data/role_connection_data.rb +69 -0
  73. data/lib/onyxcord/data/role_subscription.rb +41 -0
  74. data/lib/onyxcord/data/scheduled_event.rb +513 -0
  75. data/lib/onyxcord/data/server.rb +1614 -0
  76. data/lib/onyxcord/data/server_preview.rb +68 -0
  77. data/lib/onyxcord/data/snapshot.rb +112 -0
  78. data/lib/onyxcord/data/team.rb +98 -0
  79. data/lib/onyxcord/data/timestamp.rb +69 -0
  80. data/lib/onyxcord/data/user.rb +324 -0
  81. data/lib/onyxcord/data/voice_region.rb +46 -0
  82. data/lib/onyxcord/data/voice_state.rb +41 -0
  83. data/lib/onyxcord/data/webhook.rb +238 -0
  84. data/lib/onyxcord/data.rb +57 -0
  85. data/lib/onyxcord/errors.rb +246 -0
  86. data/lib/onyxcord/event_executor.rb +80 -0
  87. data/lib/onyxcord/events/await.rb +48 -0
  88. data/lib/onyxcord/events/bans.rb +60 -0
  89. data/lib/onyxcord/events/channels.rb +225 -0
  90. data/lib/onyxcord/events/generic.rb +129 -0
  91. data/lib/onyxcord/events/guilds.rb +269 -0
  92. data/lib/onyxcord/events/integrations.rb +100 -0
  93. data/lib/onyxcord/events/interactions.rb +624 -0
  94. data/lib/onyxcord/events/invites.rb +127 -0
  95. data/lib/onyxcord/events/lifetime.rb +31 -0
  96. data/lib/onyxcord/events/members.rb +110 -0
  97. data/lib/onyxcord/events/message.rb +399 -0
  98. data/lib/onyxcord/events/polls.rb +118 -0
  99. data/lib/onyxcord/events/presence.rb +131 -0
  100. data/lib/onyxcord/events/raw.rb +74 -0
  101. data/lib/onyxcord/events/reactions.rb +218 -0
  102. data/lib/onyxcord/events/roles.rb +87 -0
  103. data/lib/onyxcord/events/scheduled_events.rb +171 -0
  104. data/lib/onyxcord/events/threads.rb +100 -0
  105. data/lib/onyxcord/events/typing.rb +73 -0
  106. data/lib/onyxcord/events/voice_server_update.rb +48 -0
  107. data/lib/onyxcord/events/voice_state_update.rb +106 -0
  108. data/lib/onyxcord/events/webhooks.rb +65 -0
  109. data/lib/onyxcord/gateway.rb +890 -0
  110. data/lib/onyxcord/id_object.rb +39 -0
  111. data/lib/onyxcord/light/data.rb +62 -0
  112. data/lib/onyxcord/light/integrations.rb +73 -0
  113. data/lib/onyxcord/light/light_bot.rb +58 -0
  114. data/lib/onyxcord/light.rb +8 -0
  115. data/lib/onyxcord/logger.rb +120 -0
  116. data/lib/onyxcord/message_components.rb +70 -0
  117. data/lib/onyxcord/paginator.rb +60 -0
  118. data/lib/onyxcord/permissions.rb +255 -0
  119. data/lib/onyxcord/rate_limiter/gateway.rb +42 -0
  120. data/lib/onyxcord/rate_limiter/rest.rb +89 -0
  121. data/lib/onyxcord/version.rb +7 -0
  122. data/lib/onyxcord/voice/encoder.rb +115 -0
  123. data/lib/onyxcord/voice/network.rb +380 -0
  124. data/lib/onyxcord/voice/opcodes.rb +29 -0
  125. data/lib/onyxcord/voice/sodium.rb +157 -0
  126. data/lib/onyxcord/voice/timer.rb +19 -0
  127. data/lib/onyxcord/voice/voice_bot.rb +386 -0
  128. data/lib/onyxcord/webhooks.rb +14 -0
  129. data/lib/onyxcord/websocket.rb +62 -0
  130. data/lib/onyxcord.rb +180 -0
  131. data/onyxcord-webhooks.gemspec +30 -0
  132. data/onyxcord.gemspec +50 -0
  133. metadata +421 -0
@@ -0,0 +1,1141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'onyxcord/webhooks'
4
+ require 'onyxcord/message_components'
5
+
6
+ module OnyxCord
7
+ # Base class for interaction objects.
8
+ class Interaction
9
+ include IDObject
10
+
11
+ # Interaction types.
12
+ # @see https://discord.com/developers/docs/interactions/slash-commands#interaction-interactiontype
13
+ TYPES = {
14
+ ping: 1,
15
+ command: 2,
16
+ component: 3,
17
+ autocomplete: 4,
18
+ modal_submit: 5
19
+ }.freeze
20
+
21
+ # Interaction response types.
22
+ # @see https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactioncallbacktype
23
+ CALLBACK_TYPES = {
24
+ pong: 1,
25
+ channel_message: 4,
26
+ deferred_message: 5,
27
+ deferred_update: 6,
28
+ update_message: 7,
29
+ autocomplete: 8,
30
+ modal: 9
31
+ }.freeze
32
+
33
+ # Interaction context types.
34
+ # @see https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types
35
+ CONTEXTS = {
36
+ server: 0,
37
+ bot_dm: 1,
38
+ private_channel: 2
39
+ }.freeze
40
+
41
+ # Application integration types.
42
+ # @see https://discord.com/developers/docs/resources/application#application-object-application-integration-types
43
+ INTEGRATION_TYPES = {
44
+ server: 0,
45
+ user: 1
46
+ }.freeze
47
+
48
+ # @return [User, Member] The user that initiated the interaction.
49
+ attr_reader :user
50
+
51
+ # @return [Integer, nil] The ID of the server this interaction originates from.
52
+ attr_reader :server_id
53
+
54
+ # @return [Integer] The ID of the channel this interaction originates from.
55
+ attr_reader :channel_id
56
+
57
+ # @return [Channel] The channel where this interaction originates from.
58
+ attr_reader :channel
59
+
60
+ # @return [Integer] The ID of the application associated with this interaction.
61
+ attr_reader :application_id
62
+
63
+ # @return [String] The interaction token.
64
+ attr_reader :token
65
+
66
+ # @!visibility private
67
+ # @return [Integer] Currently pointless
68
+ attr_reader :version
69
+
70
+ # @return [Integer] The type of this interaction.
71
+ # @see TYPES
72
+ attr_reader :type
73
+
74
+ # @return [Hash] The interaction data.
75
+ attr_reader :data
76
+
77
+ # @return [Interactions::Message, nil] The message associated with this interaction.
78
+ attr_reader :message
79
+
80
+ # @return [Array<ActionRow>] The modal components associated with this interaction.
81
+ attr_reader :components
82
+
83
+ # @return [Permissions] The permissions the application has where this interaction originates from.
84
+ attr_reader :application_permissions
85
+
86
+ # @return [String] The selected language of the user that initiated this interaction.
87
+ attr_reader :user_locale
88
+
89
+ # @return [String, nil] The selected language of the server this interaction originates from.
90
+ attr_reader :server_locale
91
+
92
+ # @return [Integer] The context of where this interaction was initiated from.
93
+ attr_reader :context
94
+
95
+ # @return [Integer] The maximum number of bytes an attachment can have when responding to this interaction.
96
+ attr_reader :max_attachment_size
97
+
98
+ # @return [Array<Symbol>] The features of the server where this interaction was initiated from.
99
+ attr_reader :server_features
100
+
101
+ # @!visibility private
102
+ def initialize(data, bot)
103
+ @bot = bot
104
+
105
+ @id = data['id'].to_i
106
+ @application_id = data['application_id'].to_i
107
+ @type = data['type']
108
+ @message = Interactions::Message.new(data['message'], @bot, self) if data['message']
109
+ @data = data['data']
110
+ @server_id = data['guild_id']&.to_i
111
+ @channel_id = data['channel_id']&.to_i
112
+ @channel = bot.ensure_channel(data['channel']) if data['channel']
113
+ @user = begin
114
+ if data['member'] && data['member']['user']
115
+ data['member']['guild_id'] = @server_id
116
+ server = bot.servers ? bot.servers[@server_id] : nil
117
+ OnyxCord::Member.new(data['member'], server, bot)
118
+ elsif data['user']
119
+ bot.ensure_user(data['user'])
120
+ else
121
+ nil
122
+ end
123
+ rescue StandardError => e
124
+ OnyxCord::LOGGER.error("Failed to parse interaction user/member: #{e}")
125
+ nil
126
+ end
127
+ @token = data['token']
128
+ @version = data['version']
129
+ @components = @data['components']&.filter_map { |component| Components.from_data(component, @bot) } || []
130
+ @application_permissions = Permissions.new(data['app_permissions']) if data['app_permissions']
131
+ @user_locale = data['locale']
132
+ @server_locale = data['guild_locale']
133
+ @context = data['context']
134
+ @max_attachment_size = data['attachment_size_limit']
135
+ @integration_owners = data['authorizing_integration_owners']&.to_h { |key, value| [key.to_i, value.to_i] }
136
+ @server_features = data['guild'] ? data['guild']['features']&.map { |feature| feature.downcase.to_sym } : []
137
+ end
138
+
139
+ # Respond to the creation of this interaction. An interaction must be responded to or deferred,
140
+ # The response may be modified with {Interaction#edit_response} or deleted with {Interaction#delete_response}.
141
+ # Further messages can be sent with {Interaction#send_message}.
142
+ # @param content [String] The content of the message.
143
+ # @param tts [true, false]
144
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
145
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
146
+ # @param flags [Integer] Message flags.
147
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
148
+ # @param wait [true, false] Whether this method should return a Message object of the interaction response.
149
+ # @param components [Array<#to_h>] An array of components.
150
+ # @param attachments [Array<File>] Files that can be referenced in embeds and components via `attachment://file.png`.
151
+ # @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content, polls, and embeds.
152
+ # @param poll [Hash, Poll::Builder, Poll, nil] The poll that should be attached to this message.
153
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
154
+ # @yieldparam view [Webhooks::View] A builder for creating interaction components.
155
+ def respond(content: nil, tts: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: nil, wait: false, components: nil, attachments: nil, has_components: false, components_v2: false, poll: nil)
156
+ flags |= 1 << 6 if ephemeral
157
+
158
+ builder = OnyxCord::Webhooks::Builder.new
159
+ view = OnyxCord::Webhooks::View.new
160
+
161
+ # Set builder defaults from parameters
162
+ prepare_builder(builder, content, embeds, allowed_mentions, poll)
163
+ yield(builder, view) if block_given?
164
+
165
+ components ||= view
166
+ flags = OnyxCord::MessageComponents.apply_v2_flag(flags, components, force: has_components || components_v2)
167
+ data = builder.to_json_hash
168
+
169
+ response = OnyxCord::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:channel_message], data[:content], tts, data[:embeds], data[:allowed_mentions], flags, components.to_a, attachments, nil, wait, data[:poll])
170
+ return unless wait
171
+
172
+ Interactions::Message.new(JSON.parse(response)['resource']['message'], @bot, self)
173
+ end
174
+
175
+ # Defer an interaction, setting a temporary response that can be later overriden by {Interaction#send_message}.
176
+ # This method is used when you want to use a single message for your response but require additional processing time, or to simply ack
177
+ # an interaction so an error is not displayed.
178
+ # @param flags [Integer] Message flags.
179
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
180
+ def defer(flags: 0, ephemeral: true)
181
+ flags |= 1 << 6 if ephemeral
182
+
183
+ OnyxCord::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:deferred_message], nil, nil, nil, nil, flags)
184
+ nil
185
+ end
186
+
187
+ # Defer an update to an interaction. This is can only currently used by Button interactions.
188
+ def defer_update
189
+ OnyxCord::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:deferred_update])
190
+ end
191
+
192
+ # Create a modal as a response.
193
+ # @param title [String] The title of the modal being shown.
194
+ # @param custom_id [String] The custom_id used to identify the modal and store data.
195
+ # @param components [Array<Component, Hash>, nil] An array of components. These can be defined through the block as well.
196
+ # @yieldparam [OnyxCord::Webhooks::Modal] A builder for the modal's components.
197
+ def show_modal(title:, custom_id:, components: nil)
198
+ if block_given?
199
+ modal_builder = OnyxCord::Webhooks::Modal.new
200
+ yield modal_builder
201
+
202
+ components = modal_builder.to_a
203
+ end
204
+
205
+ OnyxCord::API::Interaction.create_interaction_modal_response(@token, @id, custom_id, title, components.to_a) unless type == Interaction::TYPES[:modal_submit]
206
+ nil
207
+ end
208
+
209
+ # Respond to the creation of this interaction. An interaction must be responded to or deferred,
210
+ # The response may be modified with {Interaction#edit_response} or deleted with {Interaction#delete_response}.
211
+ # Further messages can be sent with {Interaction#send_message}.
212
+ # @param content [String] The content of the message.
213
+ # @param tts [true, false]
214
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
215
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
216
+ # @param flags [Integer] Message flags.
217
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
218
+ # @param wait [true, false] Whether this method should return a Message object of the interaction response.
219
+ # @param components [Array<#to_h>] An array of components.
220
+ # @param attachments [Array<File>] Files that can be referenced in embeds and components via `attachment://file.png`.
221
+ # @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content, polls, and embeds.
222
+ # @param poll [Hash, Poll::Builder, Poll, nil] The poll that should be attached to this message.
223
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
224
+ # @yieldparam view [Webhooks::View] A builder for creating interaction components.
225
+ def update_message(content: nil, tts: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: nil, wait: false, components: nil, attachments: nil, has_components: false, components_v2: false, poll: nil)
226
+ flags |= 1 << 6 if ephemeral
227
+
228
+ builder = OnyxCord::Webhooks::Builder.new
229
+ view = OnyxCord::Webhooks::View.new
230
+
231
+ prepare_builder(builder, content, embeds, allowed_mentions, poll)
232
+ yield(builder, view) if block_given?
233
+
234
+ components ||= view
235
+ flags = OnyxCord::MessageComponents.apply_v2_flag(flags, components, force: has_components || components_v2)
236
+ data = builder.to_json_hash
237
+
238
+ response = OnyxCord::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:update_message], data[:content], tts, data[:embeds], data[:allowed_mentions], flags, components.to_a, attachments, nil, wait, data[:poll])
239
+ return unless wait
240
+
241
+ Interactions::Message.new(JSON.parse(response)['resource']['message'], @bot, self)
242
+ end
243
+
244
+ # Edit the original response to this interaction.
245
+ # @param content [String] The content of the message.
246
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
247
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
248
+ # @param flags [Integer] Message flags.
249
+ # @param components [Array<#to_h>] An array of components.
250
+ # @param attachments [Array<File>] Files that can be referenced in embeds and components via `attachment://file.png`.
251
+ # @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content, polls, and embeds.
252
+ # @param poll [Hash, Poll::Builder, Poll, nil] The poll that should be attached to this message.
253
+ # @return [InteractionMessage] The updated response message.
254
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
255
+ def edit_response(content: nil, embeds: nil, allowed_mentions: nil, flags: 0, components: nil, attachments: nil, has_components: false, components_v2: false, poll: nil)
256
+ builder = OnyxCord::Webhooks::Builder.new
257
+ view = OnyxCord::Webhooks::View.new
258
+
259
+ prepare_builder(builder, content, embeds, allowed_mentions, poll)
260
+ yield(builder, view) if block_given?
261
+
262
+ components ||= view
263
+ flags = OnyxCord::MessageComponents.apply_v2_flag(flags, components, force: has_components || components_v2)
264
+ data = builder.to_json_hash
265
+ resp = OnyxCord::API::Interaction.edit_original_interaction_response(@token, @application_id, data[:content], data[:embeds], data[:allowed_mentions], components.to_a, attachments, flags, data[:poll])
266
+
267
+ Interactions::Message.new(JSON.parse(resp), @bot, self)
268
+ end
269
+
270
+ # Delete the original interaction response.
271
+ def delete_response
272
+ OnyxCord::API::Interaction.delete_original_interaction_response(@token, @application_id)
273
+ end
274
+
275
+ # @param content [String] The content of the message.
276
+ # @param tts [true, false]
277
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
278
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
279
+ # @param flags [Integer] Message flags.
280
+ # @param ephemeral [true, false] Whether this message should only be visible to the interaction initiator.
281
+ # @param attachments [Array<File>] Files that can be referenced in embeds and components via `attachment://file.png`.
282
+ # @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content, polls, and embeds.
283
+ # @param poll [Hash, Poll::Builder, Poll, nil] The poll that should be attached to this message.
284
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
285
+ def send_message(content: nil, embeds: nil, tts: false, allowed_mentions: nil, flags: 0, ephemeral: false, components: nil, attachments: nil, has_components: false, components_v2: false, poll: nil)
286
+ flags |= 64 if ephemeral
287
+
288
+ builder = OnyxCord::Webhooks::Builder.new
289
+ view = OnyxCord::Webhooks::View.new
290
+
291
+ prepare_builder(builder, content, embeds, allowed_mentions, poll)
292
+ yield(builder, view) if block_given?
293
+
294
+ components ||= view
295
+ flags = OnyxCord::MessageComponents.apply_v2_flag(flags, components, force: has_components || components_v2)
296
+ data = builder.to_json_hash
297
+
298
+ resp = OnyxCord::API::Webhook.token_execute_webhook(
299
+ @token, @application_id, true, data[:content], nil, nil, tts, nil, data[:embeds], data[:allowed_mentions], flags, components.to_a, attachments, data[:poll]
300
+ )
301
+ Interactions::Message.new(JSON.parse(resp), @bot, self)
302
+ end
303
+
304
+ # @param message [String, Integer, InteractionMessage, Message] The message created by this interaction to be edited.
305
+ # @param content [String] The message content.
306
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds for the message.
307
+ # @param allowed_mentions [Hash, AllowedMentions] Mentions that can ping on this message.
308
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`.
309
+ # @param flags [Integer] Message flags.
310
+ # @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content, polls, and embeds.
311
+ # @param poll [Hash, Poll::Builder, Poll, nil] The poll that should be attached to this message.
312
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the method overwrite builder data.
313
+ def edit_message(message, content: nil, embeds: nil, allowed_mentions: nil, components: nil, attachments: nil, flags: 0, has_components: false, components_v2: false, poll: nil)
314
+ builder = OnyxCord::Webhooks::Builder.new
315
+ view = OnyxCord::Webhooks::View.new
316
+
317
+ prepare_builder(builder, content, embeds, allowed_mentions, poll)
318
+ yield(builder, view) if block_given?
319
+
320
+ components ||= view
321
+ flags = OnyxCord::MessageComponents.apply_v2_flag(flags, components, force: has_components || components_v2)
322
+ data = builder.to_json_hash
323
+
324
+ resp = OnyxCord::API::Webhook.token_edit_message(
325
+ @token, @application_id, message.resolve_id, data[:content], data[:embeds], data[:allowed_mentions], components.to_a, attachments, flags, data[:poll]
326
+ )
327
+ Interactions::Message.new(JSON.parse(resp), @bot, self)
328
+ end
329
+
330
+ # @param message [Integer, String, InteractionMessage, Message] The message created by this interaction to be deleted.
331
+ def delete_message(message)
332
+ OnyxCord::API::Webhook.token_delete_message(@token, @application_id, message.resolve_id)
333
+ nil
334
+ end
335
+
336
+ # Show autocomplete choices as a response.
337
+ # @param choices [Array<Hash>, Hash] Array of autocomplete choices to show the user.
338
+ def show_autocomplete_choices(choices)
339
+ choices = choices.map { |name, value| { name: name, value: value } } unless choices.is_a?(Array)
340
+ OnyxCord::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:autocomplete], nil, nil, nil, nil, nil, nil, nil, choices)
341
+ nil
342
+ end
343
+
344
+ # Get the server associated with the interaction.
345
+ # @return [Server, nil] This will be nil for interactions that occur in DM channels or servers where the bot
346
+ # does not have the `bot` scope.
347
+ def server
348
+ @bot.server(@server_id)
349
+ end
350
+
351
+ # Get the button component that triggered the interaction.
352
+ # @return [Components::Button, nil] The button that triggered this interaction if applicable, otherwise `nil`.
353
+ def button
354
+ @type == TYPES[:component] ? get_component(@data['custom_id']) : nil
355
+ end
356
+
357
+ # Get the text input components associated with the interaction.
358
+ # @return [Array<TextInput>] The text input components associated with this interaction.
359
+ def text_inputs
360
+ @components.filter_map do |entity|
361
+ entity.component if entity.is_a?(Components::Label) && entity.component.is_a?(Components::TextInput)
362
+ end
363
+ end
364
+
365
+ # Get a component by its custom ID.
366
+ # @param custom_id [String] the custom ID of the component to find.
367
+ # @return [TextInput, Button, SelectMenu, Checkbox, ModalActionGroup, nil] The component associated with the custom ID, or `nil`.
368
+ def get_component(custom_id)
369
+ components = flatten_components((@message&.components || []) + @components)
370
+ components.find { |component| component.respond_to?(:custom_id) && component.custom_id == custom_id }
371
+ end
372
+
373
+ # @return [true, false] whether the application was installed by the user who initiated this interaction.
374
+ def user_integration?
375
+ @integration_owners[1] == @user.id
376
+ end
377
+
378
+ # @return [true, false] whether the application was installed by the server where this interaction originates from.
379
+ def server_integration?
380
+ @server_id ? @integration_owners[0] == @server_id : false
381
+ end
382
+
383
+ private
384
+
385
+ # Set builder defaults from parameters
386
+ # @param builder [OnyxCord::Webhooks::Builder]
387
+ # @param content [String, nil]
388
+ # @param embeds [Array<Hash, OnyxCord::Webhooks::Embed>, nil]
389
+ # @param allowed_mentions [AllowedMentions, Hash, nil]
390
+ # @param poll [Poll, Poll::Builder, Hash, nil]
391
+ def prepare_builder(builder, content, embeds, allowed_mentions, poll)
392
+ builder.poll = poll
393
+ builder.content = content
394
+ builder.allowed_mentions = allowed_mentions
395
+ embeds&.each { |embed| builder << embed }
396
+ end
397
+
398
+ # @!visibility private
399
+ def flatten_components(components)
400
+ components = components.flat_map do |entity|
401
+ case entity
402
+ when Components::ActionRow
403
+ entity.components
404
+ when Components::Label
405
+ entity.component
406
+ when Components::Section
407
+ entity.accessory if entity.accessory.respond_to?(:custom_id)
408
+ when Components::Container
409
+ flatten_components(entity.components)
410
+ else
411
+ entity if entity.respond_to?(:custom_id)
412
+ end
413
+ end
414
+
415
+ components.compact
416
+ end
417
+ end
418
+
419
+ # An ApplicationCommand for slash commands.
420
+ class ApplicationCommand
421
+ # Command types. `chat_input` is a command that appears in the text input field. `user` and `message` types appear as context menus
422
+ # for the respective resource.
423
+ TYPES = {
424
+ chat_input: 1,
425
+ user: 2,
426
+ message: 3
427
+ }.freeze
428
+
429
+ # @return [Integer]
430
+ attr_reader :application_id
431
+
432
+ # @return [Integer, nil]
433
+ attr_reader :server_id
434
+
435
+ # @return [String]
436
+ attr_reader :name
437
+
438
+ # @return [String]
439
+ attr_reader :description
440
+
441
+ # @return [true, false]
442
+ attr_reader :default_permission
443
+
444
+ # @return [Hash]
445
+ attr_reader :options
446
+
447
+ # @return [Integer]
448
+ attr_reader :id
449
+
450
+ # @return [true, false]
451
+ attr_reader :nsfw
452
+
453
+ # @return [Array<Integer>]
454
+ attr_reader :contexts
455
+
456
+ # @return [Array<Integer>]
457
+ attr_reader :integration_types
458
+
459
+ # @!visibility private
460
+ def initialize(data, bot, server_id = nil)
461
+ @bot = bot
462
+ @id = data['id'].to_i
463
+ @application_id = data['application_id'].to_i
464
+ @server_id = server_id&.to_i
465
+
466
+ @name = data['name']
467
+ @description = data['description']
468
+ @default_permission = data['default_permission']
469
+ @options = data['options']
470
+ @nsfw = data['nsfw'] || false
471
+ @contexts = data['contexts'] || []
472
+ @integration_types = data['integration_types'] || []
473
+ end
474
+
475
+ # @param subcommand [String, nil] The subcommand to mention.
476
+ # @param subcommand_group [String, nil] The subcommand group to mention.
477
+ # @return [String] the layout to mention it in a message
478
+ def mention(subcommand_group: nil, subcommand: nil)
479
+ if subcommand_group && subcommand
480
+ "</#{name} #{subcommand_group} #{subcommand}:#{id}>"
481
+ elsif subcommand_group
482
+ "</#{name} #{subcommand_group}:#{id}>"
483
+ elsif subcommand
484
+ "</#{name} #{subcommand}:#{id}>"
485
+ else
486
+ "</#{name}:#{id}>"
487
+ end
488
+ end
489
+
490
+ alias_method :to_s, :mention
491
+
492
+ # @param name [String] The name to use for this command.
493
+ # @param description [String] The description of this command.
494
+ # @param default_permission [true, false] Whether this command is available with default permissions.
495
+ # @param nsfw [true, false] Whether this command should be marked as age-restricted.
496
+ # @yieldparam (see Bot#edit_application_command)
497
+ # @return (see Bot#edit_application_command)
498
+ def edit(name: nil, description: nil, default_permission: nil, nsfw: nil, &block)
499
+ @bot.edit_application_command(@id, server_id: @server_id, name: name, description: description, default_permission: default_permission, nsfw: nsfw, &block)
500
+ end
501
+
502
+ # Delete this application command.
503
+ # @return (see Bot#delete_application_command)
504
+ def delete
505
+ @bot.delete_application_command(@id, server_id: @server_id)
506
+ end
507
+
508
+ # Get the permission configuration for this application command in a specific server.
509
+ # @param server_id [Integer, String, nil] The ID of the server to fetch command permissions for.
510
+ # @return [Array<Permission>] the permissions for this application command in the given server.
511
+ def permissions(server_id: nil)
512
+ raise ArgumentError, 'A server ID must be provided for global application commands' if @server_id.nil? && server_id.nil?
513
+
514
+ response = JSON.parse(API::Application.get_application_command_permissions(@bot.token, @bot.profile.id, @server_id || server_id&.resolve_id, @id))
515
+ response['permissions'].map { |permission| Permission.new(permission, response, @bot) }
516
+ rescue OnyxCord::Errors::UnknownError
517
+ # If there aren't any explicit overwrites configured for the command, the response is a 400.
518
+ []
519
+ end
520
+
521
+ # An application command permission for a channel, member, or a role.
522
+ class Permission
523
+ # Map of permission types.
524
+ TYPES = {
525
+ role: 1,
526
+ member: 2,
527
+ channel: 3
528
+ }.freeze
529
+
530
+ # @return [Integer] the type of this permission.
531
+ # @see TYPES
532
+ attr_reader :type
533
+
534
+ # @return [Integer] the ID of the entity this permission is for.
535
+ attr_reader :target_id
536
+
537
+ # @return [Integer] the ID of the server this permission is for.
538
+ attr_reader :server_id
539
+
540
+ # @!visibility private
541
+ def initialize(data, command, bot)
542
+ @bot = bot
543
+ @type = data['type']
544
+ @target_id = data['id'].to_i
545
+ @overwrite = data['permission']
546
+ @command_id = command['id'].to_i
547
+ @server_id = command['guild_id'].to_i
548
+ @application_id = command['application_id'].to_i
549
+ end
550
+
551
+ # Whether this permission has been allowed, e.g has a green check in the UI.
552
+ # @return [true, false]
553
+ def allowed?
554
+ @overwrite == true
555
+ end
556
+
557
+ # Whether this permission has been denied, e.g has a red X-mark in the UI.
558
+ # @return [true, false]
559
+ def denied?
560
+ @overwrite == false
561
+ end
562
+
563
+ # Whether this permission is applied to the everyone role in the server.
564
+ # @return [true, false]
565
+ def everyone?
566
+ @target_id == @server_id
567
+ end
568
+
569
+ # Get the ID of the application command this permission is for.
570
+ # @return [Integer, nil] This will be `nil` if the permission is the
571
+ # default permission.
572
+ def command_id
573
+ @command_id unless default?
574
+ end
575
+
576
+ # Whether this permission is the default for all commands that don't
577
+ # contain explicit permission oerwrites.
578
+ # @return [true, false]
579
+ def default?
580
+ @command_id == @application_id
581
+ end
582
+
583
+ # Whether this permission is applied to every channel in the server.
584
+ # @return [true, false]
585
+ def all_channels?
586
+ @target_id == (@server_id - 1)
587
+ end
588
+
589
+ # Get the user, role, or channel(s) that this permission targets.
590
+ # @return [Array<Channel>, Role, Member]
591
+ def target
592
+ case @type
593
+ when TYPES[:role]
594
+ @bot.server(@server_id).role(@target_id)
595
+ when TYPES[:member]
596
+ @bot.server(@server_id).member(@target_id)
597
+ when TYPES[:channel]
598
+ all_channels? ? @bot.server(@server_id).channels : [@bot.channel(@target_id)]
599
+ end
600
+ end
601
+
602
+ alias_method :targets, :target
603
+
604
+ # @!method role?
605
+ # @return [true, false] whether this permission is for a role.
606
+ # @!method member?
607
+ # @return [true, false] whether this permission is for a member.
608
+ # @!method channel?
609
+ # @return [true, false] whether this permission is for a channel.
610
+ TYPES.each do |name, value|
611
+ define_method("#{name}?") do
612
+ @type == value
613
+ end
614
+ end
615
+ end
616
+ end
617
+
618
+ # Objects specific to Interactions.
619
+ module Interactions
620
+ # A builder for defining slash commands options.
621
+ class OptionBuilder
622
+ # @!visibility private
623
+ TYPES = {
624
+ subcommand: 1,
625
+ subcommand_group: 2,
626
+ string: 3,
627
+ integer: 4,
628
+ boolean: 5,
629
+ user: 6,
630
+ channel: 7,
631
+ role: 8,
632
+ mentionable: 9,
633
+ number: 10,
634
+ attachment: 11
635
+ }.freeze
636
+
637
+ # Channel types that can be provided to #channel
638
+ CHANNEL_TYPES = {
639
+ text: 0,
640
+ dm: 1,
641
+ voice: 2,
642
+ group_dm: 3,
643
+ category: 4,
644
+ news: 5,
645
+ store: 6,
646
+ news_thread: 10,
647
+ public_thread: 11,
648
+ private_thread: 12,
649
+ stage: 13
650
+ }.freeze
651
+
652
+ # @return [Array<Hash>]
653
+ attr_reader :options
654
+
655
+ # @!visibility private
656
+ def initialize
657
+ @options = []
658
+ end
659
+
660
+ # @param name [String, Symbol] The name of the subcommand.
661
+ # @param description [String] A description of the subcommand.
662
+ # @yieldparam [OptionBuilder]
663
+ # @return (see #option)
664
+ # @example
665
+ # bot.register_application_command(:test, 'Test command') do |cmd|
666
+ # cmd.subcommand(:echo) do |sub|
667
+ # sub.string('message', 'What to echo back', required: true)
668
+ # end
669
+ # end
670
+ def subcommand(name, description)
671
+ builder = OptionBuilder.new
672
+ yield builder if block_given?
673
+
674
+ option(TYPES[:subcommand], name, description, options: builder.to_a)
675
+ end
676
+
677
+ # @param name [String, Symbol] The name of the subcommand group.
678
+ # @param description [String] A description of the subcommand group.
679
+ # @yieldparam [OptionBuilder]
680
+ # @return (see #option)
681
+ # @example
682
+ # bot.register_application_command(:test, 'Test command') do |cmd|
683
+ # cmd.subcommand_group(:fun) do |group|
684
+ # group.subcommand(:8ball) do |sub|
685
+ # sub.string(:question, 'What do you ask the mighty 8ball?')
686
+ # end
687
+ # end
688
+ # end
689
+ def subcommand_group(name, description)
690
+ builder = OptionBuilder.new
691
+ yield builder
692
+
693
+ option(TYPES[:subcommand_group], name, description, options: builder.to_a)
694
+ end
695
+
696
+ # @param name [String, Symbol] The name of the argument.
697
+ # @param description [String] A description of the argument.
698
+ # @param required [true, false] Whether this option must be provided.
699
+ # @param min_length [Integer] A minimum length for option value.
700
+ # @param max_length [Integer] A maximum length for option value.
701
+ # @param choices [Hash, nil] Available choices, mapped as `Name => Value`.
702
+ # @param autocomplete [true, false] Whether this option can dynamically show choices.
703
+ # @return (see #option)
704
+ def string(name, description, required: nil, min_length: nil, max_length: nil, choices: nil, autocomplete: nil)
705
+ option(TYPES[:string], name, description,
706
+ required: required, min_length: min_length, max_length: max_length, choices: choices, autocomplete: autocomplete)
707
+ end
708
+
709
+ # @param name [String, Symbol] The name of the argument.
710
+ # @param description [String] A description of the argument.
711
+ # @param required [true, false] Whether this option must be provided.
712
+ # @param min_value [Integer] A minimum value for option.
713
+ # @param max_value [Integer] A maximum value for option.
714
+ # @param choices [Hash, nil] Available choices, mapped as `Name => Value`.
715
+ # @param autocomplete [true, false] Whether this option can dynamically show choices.
716
+ # @return (see #option)
717
+ def integer(name, description, required: nil, min_value: nil, max_value: nil, choices: nil, autocomplete: nil)
718
+ option(TYPES[:integer], name, description,
719
+ required: required, min_value: min_value, max_value: max_value, choices: choices, autocomplete: autocomplete)
720
+ end
721
+
722
+ # @param name [String, Symbol] The name of the argument.
723
+ # @param description [String] A description of the argument.
724
+ # @param required [true, false] Whether this option must be provided.
725
+ # @return (see #option)
726
+ def boolean(name, description, required: nil)
727
+ option(TYPES[:boolean], name, description, required: required)
728
+ end
729
+
730
+ # @param name [String, Symbol] The name of the argument.
731
+ # @param description [String] A description of the argument.
732
+ # @param required [true, false] Whether this option must be provided.
733
+ # @return (see #option)
734
+ def user(name, description, required: nil)
735
+ option(TYPES[:user], name, description, required: required)
736
+ end
737
+
738
+ # @param name [String, Symbol] The name of the argument.
739
+ # @param description [String] A description of the argument.
740
+ # @param required [true, false] Whether this option must be provided.
741
+ # @param types [Array<Symbol, Integer>] See {CHANNEL_TYPES}
742
+ # @return (see #option)
743
+ def channel(name, description, required: nil, types: nil)
744
+ types = types&.collect { |type| type.is_a?(Numeric) ? type : CHANNEL_TYPES[type] }
745
+ option(TYPES[:channel], name, description, required: required, channel_types: types)
746
+ end
747
+
748
+ # @param name [String, Symbol] The name of the argument.
749
+ # @param description [String] A description of the argument.
750
+ # @param required [true, false] Whether this option must be provided.
751
+ # @return (see #option)
752
+ def role(name, description, required: nil)
753
+ option(TYPES[:role], name, description, required: required)
754
+ end
755
+
756
+ # @param name [String, Symbol] The name of the argument.
757
+ # @param description [String] A description of the argument.
758
+ # @param required [true, false] Whether this option must be provided.
759
+ # @return (see #option)
760
+ def mentionable(name, description, required: nil)
761
+ option(TYPES[:mentionable], name, description, required: required)
762
+ end
763
+
764
+ # @param name [String, Symbol] The name of the argument.
765
+ # @param description [String] A description of the argument.
766
+ # @param required [true, false] Whether this option must be provided.
767
+ # @param min_value [Float] A minimum value for option.
768
+ # @param max_value [Float] A maximum value for option.
769
+ # @param autocomplete [true, false] Whether this option can dynamically show choices.
770
+ # @return (see #option)
771
+ def number(name, description, required: nil, min_value: nil, max_value: nil, choices: nil, autocomplete: nil)
772
+ option(TYPES[:number], name, description,
773
+ required: required, min_value: min_value, max_value: max_value, choices: choices, autocomplete: autocomplete)
774
+ end
775
+
776
+ # @param name [String, Symbol] The name of the argument.
777
+ # @param description [String] A description of the argument.
778
+ # @param required [true, false] Whether this option must be provided.
779
+ # @return (see #option)
780
+ def attachment(name, description, required: nil)
781
+ option(TYPES[:attachment], name, description, required: required)
782
+ end
783
+
784
+ # @!visibility private
785
+ # @param type [Integer] The argument type.
786
+ # @param name [String, Symbol] The name of the argument.
787
+ # @param description [String] A description of the argument.
788
+ # @param required [true, false] Whether this option must be provided.
789
+ # @param min_value [Integer, Float] A minimum value for integer and number options.
790
+ # @param max_value [Integer, Float] A maximum value for integer and number options.
791
+ # @param min_length [Integer] A minimum length for string option value.
792
+ # @param max_length [Integer] A maximum length for string option value.
793
+ # @param channel_types [Array<Integer>] Channel types that can be provides for channel options.
794
+ # @param autocomplete [true, false] Whether this option can dynamically show options.
795
+ # @return Hash
796
+ def option(type, name, description, required: nil, choices: nil, options: nil, min_value: nil, max_value: nil,
797
+ min_length: nil, max_length: nil, channel_types: nil, autocomplete: nil)
798
+ opt = { type: type, name: name, description: description }
799
+ choices = choices.map { |option_name, value| { name: option_name, value: value } } if choices
800
+
801
+ opt.merge!({ required: required, choices: choices, options: options, min_value: min_value,
802
+ max_value: max_value, min_length: min_length, max_length: max_length,
803
+ channel_types: channel_types, autocomplete: autocomplete }.compact)
804
+
805
+ @options << opt
806
+ opt
807
+ end
808
+
809
+ # @return [Array<Hash>]
810
+ def to_a
811
+ @options
812
+ end
813
+ end
814
+
815
+ # Builder for creating server application command permissions.
816
+ # @deprecated This system is being replaced in the near future.
817
+ class PermissionBuilder
818
+ # Role permission type
819
+ ROLE = 1
820
+ # User permission type
821
+ USER = 2
822
+
823
+ # @!visibility hidden
824
+ def initialize
825
+ @permissions = []
826
+ end
827
+
828
+ # Allow a role to use this command.
829
+ # @param role_id [Integer]
830
+ # @return [PermissionBuilder]
831
+ def allow_role(role_id)
832
+ create_entry(role_id, ROLE, true)
833
+ end
834
+
835
+ # Deny a role usage of this command.
836
+ # @param role_id [Integer]
837
+ # @return [PermissionBuilder]
838
+ def deny_role(role_id)
839
+ create_entry(role_id, ROLE, false)
840
+ end
841
+
842
+ # Allow a user to use this command.
843
+ # @param user_id [Integer]
844
+ # @return [PermissionBuilder]
845
+ def allow_user(user_id)
846
+ create_entry(user_id, USER, true)
847
+ end
848
+
849
+ # Deny a user usage of this command.
850
+ # @param user_id [Integer]
851
+ # @return [PermissionBuilder]
852
+ def deny_user(user_id)
853
+ create_entry(user_id, USER, false)
854
+ end
855
+
856
+ # Allow an entity to use this command.
857
+ # @param object [Role, User, Member]
858
+ # @return [PermissionBuilder]
859
+ # @raise [ArgumentError]
860
+ def allow(object)
861
+ case object
862
+ when OnyxCord::User, OnyxCord::Member
863
+ create_entry(object.id, USER, true)
864
+ when OnyxCord::Role
865
+ create_entry(object.id, ROLE, true)
866
+ else
867
+ raise ArgumentError, "Unable to create permission for unknown type: #{object.class}"
868
+ end
869
+ end
870
+
871
+ # Deny an entity usage of this command.
872
+ # @param object [Role, User, Member]
873
+ # @return [PermissionBuilder]
874
+ # @raise [ArgumentError]
875
+ def deny(object)
876
+ case object
877
+ when OnyxCord::User, OnyxCord::Member
878
+ create_entry(object.id, USER, false)
879
+ when OnyxCord::Role
880
+ create_entry(object.id, ROLE, false)
881
+ else
882
+ raise ArgumentError, "Unable to create permission for unknown type: #{object.class}"
883
+ end
884
+ end
885
+
886
+ # @!visibility private
887
+ # @return [Array<Hash>]
888
+ def to_a
889
+ @permissions
890
+ end
891
+
892
+ private
893
+
894
+ def create_entry(id, type, permission)
895
+ @permissions << { id: id, type: type, permission: permission }
896
+ self
897
+ end
898
+ end
899
+
900
+ # A message partial for interactions.
901
+ class Message
902
+ include IDObject
903
+
904
+ # @return [Interaction] The interaction that created this message.
905
+ attr_reader :interaction
906
+
907
+ # @return [String, nil] The content of the message.
908
+ attr_reader :content
909
+
910
+ # @return [true, false] Whether this message is pinned in the channel it belongs to.
911
+ attr_reader :pinned
912
+
913
+ # @return [true, false]
914
+ attr_reader :tts
915
+
916
+ # @return [Time]
917
+ attr_reader :timestamp
918
+
919
+ # @return [Time, nil]
920
+ attr_reader :edited_timestamp
921
+
922
+ # @return [true, false]
923
+ attr_reader :edited
924
+
925
+ # @return [Integer]
926
+ attr_reader :id
927
+
928
+ # @return [User] The user of the application.
929
+ attr_reader :author
930
+
931
+ # @return [Attachment]
932
+ attr_reader :attachments
933
+
934
+ # @return [Array<Embed>]
935
+ attr_reader :embeds
936
+
937
+ # @return [Array<User>]
938
+ attr_reader :mentions
939
+
940
+ # @return [Integer]
941
+ attr_reader :flags
942
+
943
+ # @return [Integer]
944
+ attr_reader :channel_id
945
+
946
+ # @return [Hash, nil]
947
+ attr_reader :message_reference
948
+
949
+ # @return [Array<Component>]
950
+ attr_reader :components
951
+
952
+ # @!visibility private
953
+ def initialize(data, bot, interaction)
954
+ @data = data
955
+ @bot = bot
956
+ @interaction = interaction
957
+ @content = data['content']
958
+ @channel_id = data['channel_id'].to_i
959
+ @pinned = data['pinned']
960
+ @tts = data['tts']
961
+
962
+ @message_reference = data['message_reference']
963
+
964
+ @server_id = @interaction.server_id
965
+
966
+ @timestamp = Time.parse(data['timestamp']) if data['timestamp']
967
+ @edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
968
+ @edited = !@edited_timestamp.nil?
969
+
970
+ @id = data['id'].to_i
971
+
972
+ @author = bot.ensure_user(data['author'] || data['member']['user'])
973
+
974
+ @attachments = []
975
+ @attachments = data['attachments'].map { |e| Attachment.new(e, self, @bot) } if data['attachments']
976
+
977
+ @embeds = []
978
+ @embeds = data['embeds'].map { |e| Embed.new(e, self) } if data['embeds']
979
+
980
+ @mentions = []
981
+
982
+ data['mentions']&.each do |element|
983
+ @mentions << bot.ensure_user(element)
984
+ end
985
+
986
+ @mention_roles = data['mention_roles']
987
+ @mention_everyone = data['mention_everyone']
988
+ @flags = data['flags']
989
+ @pinned = data['pinned']
990
+ @components = data['components']&.filter_map { |component| Components.from_data(component, @bot) } || []
991
+ end
992
+
993
+ # @return [Member, nil] This will return nil if the bot does not have access to the
994
+ # server the interaction originated in.
995
+ def member
996
+ server&.member(@user.id)
997
+ end
998
+
999
+ # @return [Server, nil] This will return nil if the bot does not have access to the
1000
+ # server the interaction originated in.
1001
+ def server
1002
+ @bot.server(@server_id)
1003
+ end
1004
+
1005
+ # @return [Channel] The channel the interaction originates from.
1006
+ # @raise [Errors::NoPermission] When the bot is not in the server associated with this interaction.
1007
+ def channel
1008
+ @bot.channel(@channel_id)
1009
+ end
1010
+
1011
+ # Respond to this message.
1012
+ # @param (see Interaction#send_message)
1013
+ # @yieldparam (see Interaction#send_message)
1014
+ def respond(content: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: true, components: nil, attachments: nil, &block)
1015
+ @interaction.send_message(content: content, embeds: embeds, allowed_mentions: allowed_mentions, flags: flags, ephemeral: ephemeral, components: components, attachments: attachments, &block)
1016
+ end
1017
+
1018
+ # Delete this message.
1019
+ def delete
1020
+ @interaction.delete_message(@id)
1021
+ end
1022
+
1023
+ # Edit this message's data.
1024
+ # @param content (see Interaction#send_message)
1025
+ # @param embeds (see Interaction#send_message)
1026
+ # @param allowed_mentions (see Interaction#send_message)
1027
+ # @yieldparam (see Interaction#send_message)
1028
+ def edit(content: nil, embeds: nil, allowed_mentions: nil, components: nil, attachments: nil, &block)
1029
+ @interaction.edit_message(@id, content: content, embeds: embeds, allowed_mentions: allowed_mentions, components: components, attachments: attachments, &block)
1030
+ end
1031
+
1032
+ # @return [OnyxCord::Message]
1033
+ def to_message
1034
+ OnyxCord::Message.new(@data, @bot)
1035
+ end
1036
+
1037
+ alias_method :message, :to_message
1038
+
1039
+ # @!visibility private
1040
+ def inspect
1041
+ "<Interaction::Message content=#{@content.inspect} embeds=#{@embeds.inspect} channel_id=#{@channel_id} server_id=#{@server_id} author=#{@author.inspect}>"
1042
+ end
1043
+ end
1044
+
1045
+ # Supplemental metadata about an interaction.
1046
+ class Metadata
1047
+ include IDObject
1048
+
1049
+ # @return [Integer] the type of the interaction.
1050
+ attr_reader :type
1051
+
1052
+ # @return [User] the user that initiated the interaction.
1053
+ attr_reader :user
1054
+
1055
+ # @return [User, nil] the user that the command was ran on.
1056
+ attr_reader :target_user
1057
+
1058
+ # @return [Integer, nil] the ID of the message the command was ran on.
1059
+ attr_reader :target_message_id
1060
+
1061
+ # @return [Metadata, nil] the metadata for the interaction that opened the modal.
1062
+ attr_reader :triggering_metadata
1063
+
1064
+ # @return [Integer, nil] the ID of the message that contained the interactive message component.
1065
+ attr_reader :interacted_message_id
1066
+
1067
+ # @return [Integer, nil] the ID the original response message; only present on follow-up messages.
1068
+ attr_reader :original_response_message_id
1069
+
1070
+ # @!visibility private
1071
+ def initialize(data, message, bot)
1072
+ @bot = bot
1073
+ @message = message
1074
+ @id = data['id'].to_i
1075
+ @type = data['type']
1076
+ @user = bot.ensure_user(data['user']) if data['user']
1077
+ @target_user = bot.ensure_user(data['target_user']) if data['target_user']
1078
+ @target_message_id = data['target_message_id']&.to_i
1079
+ @triggering_metadata = Metadata.new(data['triggering_interaction_metadata'], @message, @bot) if data['triggering_interaction_metadata']
1080
+ @interacted_message_id = data['interacted_message_id']&.to_i
1081
+ @original_response_message_id = data['original_response_message_id']&.to_i
1082
+ @integration_owners = data['authorizing_integration_owners']&.to_h { |key, value| [key.to_i, value.to_i] }
1083
+ end
1084
+
1085
+ # Check if the interaction was triggered by a user by installed the application.
1086
+ # @return [true, false] whether or not the application was installed by the user
1087
+ # who initiated this interaction.
1088
+ def user_integration?
1089
+ @integration_owners[1] == @user.id
1090
+ end
1091
+
1092
+ # Check if the interaction was triggered by a server by installed the application.
1093
+ # @return [true, false] whether or not the application was installed by the server
1094
+ # where this interaction originates from.
1095
+ def server_integration?
1096
+ @integration_owners[0] == @message.server.id
1097
+ end
1098
+
1099
+ # Attempt to fetch the target message of the interaction.
1100
+ # @return [Message, nil] the target message of the interaction, or `nil` if it couldn't be found.
1101
+ def target_message
1102
+ return unless @target_message_id
1103
+
1104
+ @target_message ||= @message.channel.message(@target_message_id)
1105
+ end
1106
+
1107
+ # Attempt to fetch the message that contained the interatctive component.
1108
+ # @return [Message, nil] the interacted message with the component, or `nil` if it couldn't be found.
1109
+ def interacted_message
1110
+ return unless @interacted_message_id
1111
+
1112
+ @interacted_message ||= @message.channel.message(@interacted_message_id)
1113
+ end
1114
+
1115
+ # Attempt to fetch the original response message of the interaction.
1116
+ # @return [Message, nil] the original response message of the interaction, or `nil` if it couldn't be found.
1117
+ def original_response_message
1118
+ return unless @original_response_message_id
1119
+
1120
+ @original_response_message ||= @message.channel.message(@original_response_message_id)
1121
+ end
1122
+
1123
+ # @!method command?
1124
+ # @return [true, false] whether or not the interaction metadata is for an application command.
1125
+ # @!method component?
1126
+ # @return [true, false] whether or not the interaction metadata is for a message component.
1127
+ # @!method modal_submit?
1128
+ # @return [true, false] whether or not the interaction metadata is for a modal submission.
1129
+ Interaction::TYPES.each do |name, value|
1130
+ define_method("#{name}?") do
1131
+ @type == value
1132
+ end
1133
+ end
1134
+
1135
+ # @!visibility private
1136
+ def inspect
1137
+ "<Interactions::Metadata id=#{@id} type=#{@type} user=#{@user.inspect} target_user=#{@target_user.inspect}>"
1138
+ end
1139
+ end
1140
+ end
1141
+ end