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.
- checksums.yaml +7 -0
- data/.devcontainer/Dockerfile +13 -0
- data/.devcontainer/devcontainer.json +29 -0
- data/.devcontainer/postcreate.sh +4 -0
- data/.github/CONTRIBUTING.md +13 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- data/.github/pull_request_template.md +37 -0
- data/.github/workflows/ci.yml +78 -0
- data/.github/workflows/codeql.yml +65 -0
- data/.github/workflows/deploy.yml +54 -0
- data/.github/workflows/release.yml +51 -0
- data/.gitignore +16 -0
- data/.markdownlint.json +4 -0
- data/.overcommit.yml +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +129 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +305 -0
- data/Rakefile +17 -0
- data/bin/console +15 -0
- data/bin/setup +7 -0
- data/lib/onyxcord/allowed_mentions.rb +43 -0
- data/lib/onyxcord/api/application.rb +316 -0
- data/lib/onyxcord/api/channel.rb +700 -0
- data/lib/onyxcord/api/interaction.rb +67 -0
- data/lib/onyxcord/api/invite.rb +44 -0
- data/lib/onyxcord/api/server.rb +775 -0
- data/lib/onyxcord/api/user.rb +158 -0
- data/lib/onyxcord/api/webhook.rb +163 -0
- data/lib/onyxcord/api.rb +335 -0
- data/lib/onyxcord/await.rb +51 -0
- data/lib/onyxcord/bot.rb +1971 -0
- data/lib/onyxcord/cache.rb +326 -0
- data/lib/onyxcord/colour_rgb.rb +43 -0
- data/lib/onyxcord/commands/command_bot.rb +511 -0
- data/lib/onyxcord/commands/container.rb +112 -0
- data/lib/onyxcord/commands/events.rb +11 -0
- data/lib/onyxcord/commands/parser.rb +327 -0
- data/lib/onyxcord/commands/rate_limiter.rb +144 -0
- data/lib/onyxcord/configuration.rb +125 -0
- data/lib/onyxcord/container.rb +988 -0
- data/lib/onyxcord/data/activity.rb +271 -0
- data/lib/onyxcord/data/application.rb +341 -0
- data/lib/onyxcord/data/attachment.rb +91 -0
- data/lib/onyxcord/data/audit_logs.rb +438 -0
- data/lib/onyxcord/data/avatar_decoration.rb +26 -0
- data/lib/onyxcord/data/call.rb +22 -0
- data/lib/onyxcord/data/channel.rb +1355 -0
- data/lib/onyxcord/data/channel_tag.rb +69 -0
- data/lib/onyxcord/data/collectibles.rb +47 -0
- data/lib/onyxcord/data/component.rb +583 -0
- data/lib/onyxcord/data/embed.rb +258 -0
- data/lib/onyxcord/data/emoji.rb +123 -0
- data/lib/onyxcord/data/install_params.rb +24 -0
- data/lib/onyxcord/data/integration.rb +144 -0
- data/lib/onyxcord/data/interaction.rb +1141 -0
- data/lib/onyxcord/data/invite.rb +137 -0
- data/lib/onyxcord/data/member.rb +528 -0
- data/lib/onyxcord/data/message.rb +612 -0
- data/lib/onyxcord/data/message_activity.rb +41 -0
- data/lib/onyxcord/data/overwrite.rb +109 -0
- data/lib/onyxcord/data/poll.rb +365 -0
- data/lib/onyxcord/data/primary_server.rb +60 -0
- data/lib/onyxcord/data/profile.rb +79 -0
- data/lib/onyxcord/data/reaction.rb +64 -0
- data/lib/onyxcord/data/recipient.rb +34 -0
- data/lib/onyxcord/data/role.rb +449 -0
- data/lib/onyxcord/data/role_connection_data.rb +69 -0
- data/lib/onyxcord/data/role_subscription.rb +41 -0
- data/lib/onyxcord/data/scheduled_event.rb +513 -0
- data/lib/onyxcord/data/server.rb +1614 -0
- data/lib/onyxcord/data/server_preview.rb +68 -0
- data/lib/onyxcord/data/snapshot.rb +112 -0
- data/lib/onyxcord/data/team.rb +98 -0
- data/lib/onyxcord/data/timestamp.rb +69 -0
- data/lib/onyxcord/data/user.rb +324 -0
- data/lib/onyxcord/data/voice_region.rb +46 -0
- data/lib/onyxcord/data/voice_state.rb +41 -0
- data/lib/onyxcord/data/webhook.rb +238 -0
- data/lib/onyxcord/data.rb +57 -0
- data/lib/onyxcord/errors.rb +246 -0
- data/lib/onyxcord/event_executor.rb +80 -0
- data/lib/onyxcord/events/await.rb +48 -0
- data/lib/onyxcord/events/bans.rb +60 -0
- data/lib/onyxcord/events/channels.rb +225 -0
- data/lib/onyxcord/events/generic.rb +129 -0
- data/lib/onyxcord/events/guilds.rb +269 -0
- data/lib/onyxcord/events/integrations.rb +100 -0
- data/lib/onyxcord/events/interactions.rb +624 -0
- data/lib/onyxcord/events/invites.rb +127 -0
- data/lib/onyxcord/events/lifetime.rb +31 -0
- data/lib/onyxcord/events/members.rb +110 -0
- data/lib/onyxcord/events/message.rb +399 -0
- data/lib/onyxcord/events/polls.rb +118 -0
- data/lib/onyxcord/events/presence.rb +131 -0
- data/lib/onyxcord/events/raw.rb +74 -0
- data/lib/onyxcord/events/reactions.rb +218 -0
- data/lib/onyxcord/events/roles.rb +87 -0
- data/lib/onyxcord/events/scheduled_events.rb +171 -0
- data/lib/onyxcord/events/threads.rb +100 -0
- data/lib/onyxcord/events/typing.rb +73 -0
- data/lib/onyxcord/events/voice_server_update.rb +48 -0
- data/lib/onyxcord/events/voice_state_update.rb +106 -0
- data/lib/onyxcord/events/webhooks.rb +65 -0
- data/lib/onyxcord/gateway.rb +890 -0
- data/lib/onyxcord/id_object.rb +39 -0
- data/lib/onyxcord/light/data.rb +62 -0
- data/lib/onyxcord/light/integrations.rb +73 -0
- data/lib/onyxcord/light/light_bot.rb +58 -0
- data/lib/onyxcord/light.rb +8 -0
- data/lib/onyxcord/logger.rb +120 -0
- data/lib/onyxcord/message_components.rb +70 -0
- data/lib/onyxcord/paginator.rb +60 -0
- data/lib/onyxcord/permissions.rb +255 -0
- data/lib/onyxcord/rate_limiter/gateway.rb +42 -0
- data/lib/onyxcord/rate_limiter/rest.rb +89 -0
- data/lib/onyxcord/version.rb +7 -0
- data/lib/onyxcord/voice/encoder.rb +115 -0
- data/lib/onyxcord/voice/network.rb +380 -0
- data/lib/onyxcord/voice/opcodes.rb +29 -0
- data/lib/onyxcord/voice/sodium.rb +157 -0
- data/lib/onyxcord/voice/timer.rb +19 -0
- data/lib/onyxcord/voice/voice_bot.rb +386 -0
- data/lib/onyxcord/webhooks.rb +14 -0
- data/lib/onyxcord/websocket.rb +62 -0
- data/lib/onyxcord.rb +180 -0
- data/onyxcord-webhooks.gemspec +30 -0
- data/onyxcord.gemspec +50 -0
- 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
|