discorb 0.11.1 → 0.12.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 +4 -4
- data/.github/workflows/package_register.yml +34 -0
- data/Changelog.md +23 -0
- data/README.md +4 -3
- data/discorb.gemspec +0 -2
- data/docs/cli/{init.md → new.md} +0 -0
- data/docs/cli/setup.md +1 -1
- data/docs/cli.md +1 -1
- data/docs/events.md +5 -0
- data/lib/discorb/app_command.rb +14 -0
- data/lib/discorb/channel.rb +0 -42
- data/lib/discorb/client.rb +45 -42
- data/lib/discorb/color.rb +1 -0
- data/lib/discorb/common.rb +7 -1
- data/lib/discorb/error.rb +8 -5
- data/lib/discorb/gateway.rb +60 -47
- data/lib/discorb/interaction/autocomplete.rb +49 -0
- data/lib/discorb/interaction/command.rb +142 -0
- data/lib/discorb/interaction/components.rb +85 -0
- data/lib/discorb/interaction/response.rb +188 -0
- data/lib/discorb/interaction/root.rb +94 -0
- data/lib/discorb/interaction.rb +2 -616
- data/lib/discorb/log.rb +3 -2
- data/lib/discorb/message.rb +3 -1
- data/lib/discorb/modules.rb +42 -0
- data/lib/discorb/rate_limit.rb +2 -2
- metadata +9 -4
data/lib/discorb/interaction.rb
CHANGED
@@ -1,617 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Discorb
|
4
|
-
#
|
5
|
-
# Represents a user interaction with the bot.
|
6
|
-
#
|
7
|
-
class Interaction < DiscordModel
|
8
|
-
# @return [Discorb::Snowflake] The ID of the interaction.
|
9
|
-
attr_reader :id
|
10
|
-
# @return [Discorb::Snowflake] The ID of the application that created the interaction.
|
11
|
-
attr_reader :application_id
|
12
|
-
# @return [Symbol] The type of interaction.
|
13
|
-
attr_reader :type
|
14
|
-
# @return [Discorb::Member] The member that created the interaction.
|
15
|
-
attr_reader :member
|
16
|
-
# @return [Discorb::User] The user that created the interaction.
|
17
|
-
attr_reader :user
|
18
|
-
# @return [Integer] The type of interaction.
|
19
|
-
# @note This is always `1` for now.
|
20
|
-
attr_reader :version
|
21
|
-
# @return [String] The token for the interaction.
|
22
|
-
attr_reader :token
|
23
|
-
|
24
|
-
# @!attribute [r] guild
|
25
|
-
# @macro client_cache
|
26
|
-
# @return [Discorb::Guild] The guild the interaction took place in.
|
27
|
-
# @!attribute [r] channel
|
28
|
-
# @macro client_cache
|
29
|
-
# @return [Discorb::Channel] The channel the interaction took place in.
|
30
|
-
# @!attribute [r] target
|
31
|
-
# @return [Discorb::User, Discorb::Member] The user or member the interaction took place with.
|
32
|
-
|
33
|
-
@interaction_type = nil
|
34
|
-
@interaction_name = nil
|
35
|
-
|
36
|
-
# @private
|
37
|
-
def initialize(client, data)
|
38
|
-
@client = client
|
39
|
-
@id = Snowflake.new(data[:id])
|
40
|
-
@application_id = Snowflake.new(data[:application_id])
|
41
|
-
@type = self.class.interaction_name
|
42
|
-
@type_id = self.class.interaction_type
|
43
|
-
@guild_id = data[:guild_id] && Snowflake.new(data[:guild_id])
|
44
|
-
@channel_id = data[:channel_id] && Snowflake.new(data[:channel_id])
|
45
|
-
@member = guild.members[data[:member][:id]] || Member.new(@client, @guild_id, data[:member][:user], data[:member]) if data[:member]
|
46
|
-
@user = @client.users[data[:user][:id]] || User.new(@client, data[:user]) if data[:user]
|
47
|
-
@token = data[:token]
|
48
|
-
@version = data[:version]
|
49
|
-
@defered = false
|
50
|
-
@responded = false
|
51
|
-
_set_data(data[:data])
|
52
|
-
end
|
53
|
-
|
54
|
-
def guild
|
55
|
-
@client.guilds[@guild_id]
|
56
|
-
end
|
57
|
-
|
58
|
-
def channel
|
59
|
-
@client.channels[@channel_id]
|
60
|
-
end
|
61
|
-
|
62
|
-
def target
|
63
|
-
@member || @user
|
64
|
-
end
|
65
|
-
|
66
|
-
alias fired_by target
|
67
|
-
alias from target
|
68
|
-
|
69
|
-
def inspect
|
70
|
-
"#<#{self.class} id=#{@id}>"
|
71
|
-
end
|
72
|
-
|
73
|
-
class << self
|
74
|
-
# @private
|
75
|
-
attr_reader :interaction_type, :interaction_name, :event_name
|
76
|
-
|
77
|
-
# @private
|
78
|
-
def make_interaction(client, data)
|
79
|
-
interaction = nil
|
80
|
-
descendants.each do |klass|
|
81
|
-
interaction = klass.make_interaction(client, data) if !klass.interaction_type.nil? && klass.interaction_type == data[:type]
|
82
|
-
end
|
83
|
-
if interaction.nil?
|
84
|
-
client.log.warn("Unknown interaction type #{data[:type]}, initialized Interaction")
|
85
|
-
interaction = Interaction.new(client, data)
|
86
|
-
end
|
87
|
-
interaction
|
88
|
-
end
|
89
|
-
|
90
|
-
# @private
|
91
|
-
def descendants
|
92
|
-
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
#
|
97
|
-
# A module for response with source.
|
98
|
-
#
|
99
|
-
module SourceResponse
|
100
|
-
#
|
101
|
-
# Response with `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`(`5`).
|
102
|
-
#
|
103
|
-
# @macro async
|
104
|
-
# @macro http
|
105
|
-
#
|
106
|
-
# @param [Boolean] ephemeral Whether to make the response ephemeral.
|
107
|
-
#
|
108
|
-
def defer_source(ephemeral: false)
|
109
|
-
Async do
|
110
|
-
@client.http.post("/interactions/#{@id}/#{@token}/callback", {
|
111
|
-
type: 5,
|
112
|
-
data: {
|
113
|
-
flags: (ephemeral ? 1 << 6 : 0),
|
114
|
-
},
|
115
|
-
}).wait
|
116
|
-
@defered = true
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
#
|
121
|
-
# Response with `CHANNEL_MESSAGE_WITH_SOURCE`(`4`).
|
122
|
-
#
|
123
|
-
# @macro async
|
124
|
-
# @macro http
|
125
|
-
#
|
126
|
-
# @param [String] content The content of the response.
|
127
|
-
# @param [Boolean] tts Whether to send the message as text-to-speech.
|
128
|
-
# @param [Discorb::Embed] embed The embed to send.
|
129
|
-
# @param [Array<Discorb::Embed>] embeds The embeds to send. (max: 10)
|
130
|
-
# @param [Discorb::AllowedMentions] allowed_mentions The allowed mentions to send.
|
131
|
-
# @param [Array<Discorb::Component>, Array<Array<Discorb::Component>>] components The components to send.
|
132
|
-
# @param [Boolean] ephemeral Whether to make the response ephemeral.
|
133
|
-
#
|
134
|
-
# @return [Discorb::Interaction::SourceResponse::CallbackMessage, Discorb::Webhook::Message] The callback message.
|
135
|
-
#
|
136
|
-
def post(content = nil, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, ephemeral: false)
|
137
|
-
Async do
|
138
|
-
payload = {}
|
139
|
-
payload[:content] = content if content
|
140
|
-
payload[:tts] = tts
|
141
|
-
tmp_embed = if embed
|
142
|
-
[embed]
|
143
|
-
elsif embeds
|
144
|
-
embeds
|
145
|
-
end
|
146
|
-
payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
|
147
|
-
payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
|
148
|
-
if components
|
149
|
-
tmp_components = []
|
150
|
-
tmp_row = []
|
151
|
-
components.each do |c|
|
152
|
-
case c
|
153
|
-
when Array
|
154
|
-
tmp_components << tmp_row
|
155
|
-
tmp_row = []
|
156
|
-
tmp_components << c
|
157
|
-
when SelectMenu
|
158
|
-
tmp_components << tmp_row
|
159
|
-
tmp_row = []
|
160
|
-
tmp_components << [c]
|
161
|
-
else
|
162
|
-
tmp_row << c
|
163
|
-
end
|
164
|
-
end
|
165
|
-
tmp_components << tmp_row
|
166
|
-
payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
|
167
|
-
end
|
168
|
-
payload[:flags] = (ephemeral ? 1 << 6 : 0)
|
169
|
-
|
170
|
-
ret = if @responded
|
171
|
-
_resp, data = @client.http.post("/webhooks/#{@application_id}/#{@token}", payload).wait
|
172
|
-
webhook = Webhook::URLWebhook.new("/webhooks/#{@application_id}/#{@token}")
|
173
|
-
Webhook::Message.new(webhook, data, @client)
|
174
|
-
elsif @defered
|
175
|
-
@client.http.patch("/webhooks/#{@application_id}/#{@token}/messages/@original", payload).wait
|
176
|
-
CallbackMessage.new(@client, payload, @application_id, @token)
|
177
|
-
else
|
178
|
-
@client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 4, data: payload }).wait
|
179
|
-
CallbackMessage.new(@client, payload, @application_id, @token)
|
180
|
-
end
|
181
|
-
@responded = true
|
182
|
-
ret
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
class CallbackMessage
|
187
|
-
# @private
|
188
|
-
def initialize(client, data, application_id, token)
|
189
|
-
@client = client
|
190
|
-
@data = data
|
191
|
-
@application_id = application_id
|
192
|
-
@token = token
|
193
|
-
end
|
194
|
-
|
195
|
-
#
|
196
|
-
# Edits the callback message.
|
197
|
-
# @macro async
|
198
|
-
# @macro http
|
199
|
-
# @macro edit
|
200
|
-
#
|
201
|
-
# @param [String] content The new content of the message.
|
202
|
-
# @param [Discorb::Embed] embed The new embed of the message.
|
203
|
-
# @param [Array<Discorb::Embed>] embeds The new embeds of the message.
|
204
|
-
# @param [Array<Discorb::Attachment>] attachments The attachments to remain.
|
205
|
-
# @param [Discorb::File] file The file to send.
|
206
|
-
# @param [Array<Discorb::File>] files The files to send.
|
207
|
-
#
|
208
|
-
def edit(
|
209
|
-
content = :unset,
|
210
|
-
embed: :unset, embeds: :unset,
|
211
|
-
file: :unset, files: :unset,
|
212
|
-
attachments: :unset
|
213
|
-
)
|
214
|
-
Async do
|
215
|
-
payload = {}
|
216
|
-
payload[:content] = content if content != :unset
|
217
|
-
payload[:embeds] = embed ? [embed.to_hash] : [] if embed != :unset
|
218
|
-
payload[:embeds] = embeds.map(&:to_hash) if embeds != :unset
|
219
|
-
payload[:attachments] = attachments.map(&:to_hash) if attachments != :unset
|
220
|
-
files = [file] if file != :unset
|
221
|
-
if files == :unset
|
222
|
-
headers = {
|
223
|
-
"Content-Type" => "application/json",
|
224
|
-
}
|
225
|
-
else
|
226
|
-
headers, payload = HTTP.multipart(payload, files)
|
227
|
-
end
|
228
|
-
@client.http.patch("/webhooks/#{@application_id}/#{@token}/messages/@original", payload, headers: headers).wait
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
alias modify edit
|
233
|
-
|
234
|
-
#
|
235
|
-
# Deletes the callback message.
|
236
|
-
# @note This will fail if the message is ephemeral.
|
237
|
-
#
|
238
|
-
def delete!
|
239
|
-
Async do
|
240
|
-
@client.http.delete("/webhooks/#{@application_id}/#{@token}/messages/@original").wait
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
#
|
247
|
-
# A module for response with update.
|
248
|
-
#
|
249
|
-
module UpdateResponse
|
250
|
-
#
|
251
|
-
# Response with `DEFERRED_UPDATE_MESSAGE`(`6`).
|
252
|
-
#
|
253
|
-
# @param [Boolean] ephemeral Whether to make the response ephemeral.
|
254
|
-
#
|
255
|
-
def defer_update(ephemeral: false)
|
256
|
-
Async do
|
257
|
-
@client.http.post("/interactions/#{@id}/#{@token}/callback", {
|
258
|
-
type: 6,
|
259
|
-
data: {
|
260
|
-
flags: (ephemeral ? 1 << 6 : 0),
|
261
|
-
},
|
262
|
-
}).wait
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
#
|
267
|
-
# Response with `UPDATE_MESSAGE`(`7`).
|
268
|
-
#
|
269
|
-
# @macro async
|
270
|
-
# @macro http
|
271
|
-
#
|
272
|
-
# @param [String] content The content of the response.
|
273
|
-
# @param [Boolean] tts Whether to send the message as text-to-speech.
|
274
|
-
# @param [Discorb::Embed] embed The embed to send.
|
275
|
-
# @param [Array<Discorb::Embed>] embeds The embeds to send. (max: 10)
|
276
|
-
# @param [Discorb::AllowedMentions] allowed_mentions The allowed mentions to send.
|
277
|
-
# @param [Array<Discorb::Component>, Array<Array<Discorb::Component>>] components The components to send.
|
278
|
-
# @param [Boolean] ephemeral Whether to make the response ephemeral.
|
279
|
-
#
|
280
|
-
def edit(content, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, ephemeral: false)
|
281
|
-
Async do
|
282
|
-
payload = {}
|
283
|
-
payload[:content] = content if content
|
284
|
-
payload[:tts] = tts
|
285
|
-
tmp_embed = if embed
|
286
|
-
[embed]
|
287
|
-
elsif embeds
|
288
|
-
embeds
|
289
|
-
end
|
290
|
-
payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
|
291
|
-
payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
|
292
|
-
if components
|
293
|
-
tmp_components = []
|
294
|
-
tmp_row = []
|
295
|
-
components.each do |c|
|
296
|
-
case c
|
297
|
-
when Array
|
298
|
-
tmp_components << tmp_row
|
299
|
-
tmp_row = []
|
300
|
-
tmp_components << c
|
301
|
-
when SelectMenu
|
302
|
-
tmp_components << tmp_row
|
303
|
-
tmp_row = []
|
304
|
-
tmp_components << [c]
|
305
|
-
else
|
306
|
-
tmp_row << c
|
307
|
-
end
|
308
|
-
end
|
309
|
-
tmp_components << tmp_row
|
310
|
-
payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
|
311
|
-
end
|
312
|
-
payload[:flags] = (ephemeral ? 1 << 6 : 0)
|
313
|
-
@client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 7, data: payload }).wait
|
314
|
-
end
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
private
|
319
|
-
|
320
|
-
def _set_data(*)
|
321
|
-
nil
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
#
|
326
|
-
# Represents a command interaction.
|
327
|
-
#
|
328
|
-
class CommandInteraction < Interaction
|
329
|
-
@interaction_type = 2
|
330
|
-
@interaction_name = :application_command
|
331
|
-
include Interaction::SourceResponse
|
332
|
-
|
333
|
-
#
|
334
|
-
# Represents a slash command interaction.
|
335
|
-
#
|
336
|
-
class SlashCommand < CommandInteraction
|
337
|
-
@command_type = 1
|
338
|
-
|
339
|
-
private
|
340
|
-
|
341
|
-
def _set_data(data)
|
342
|
-
super
|
343
|
-
Sync do
|
344
|
-
name = data[:name]
|
345
|
-
options = nil
|
346
|
-
if (option = data[:options]&.first)
|
347
|
-
case option[:type]
|
348
|
-
when 1
|
349
|
-
name += " #{option[:name]}"
|
350
|
-
options = option[:options]
|
351
|
-
when 2
|
352
|
-
name += " #{option[:name]}"
|
353
|
-
if (option_sub = option[:options]&.first)
|
354
|
-
if option_sub[:type] == 1
|
355
|
-
name += " #{option_sub[:name]}"
|
356
|
-
options = option_sub[:options]
|
357
|
-
else
|
358
|
-
options = option[:options]
|
359
|
-
end
|
360
|
-
end
|
361
|
-
else
|
362
|
-
options = data[:options]
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
unless (command = @client.bottom_commands.find { |c| c.to_s == name && c.type_raw == 1 })
|
367
|
-
@client.log.warn "Unknown command name #{name}, ignoring"
|
368
|
-
next
|
369
|
-
end
|
370
|
-
|
371
|
-
option_map = command.options.map { |k, v| [k.to_s, v[:default]] }.to_h
|
372
|
-
options ||= []
|
373
|
-
options.each do |option|
|
374
|
-
val = case option[:type]
|
375
|
-
when 3, 4, 5, 10
|
376
|
-
option[:value]
|
377
|
-
when 6
|
378
|
-
guild.members[option[:value]] || guild.fetch_member(option[:value]).wait
|
379
|
-
when 7
|
380
|
-
guild.channels[option[:value]] || guild.fetch_channels.wait.find { |channel| channel.id == option[:value] }
|
381
|
-
when 8
|
382
|
-
guild.roles[option[:value]] || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
|
383
|
-
when 9
|
384
|
-
guild.members[option[:value]] || guild.roles[option[:value]] || guild.fetch_member(option[:value]).wait || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
|
385
|
-
end
|
386
|
-
option_map[option[:name]] = val
|
387
|
-
end
|
388
|
-
|
389
|
-
command.block.call(self, *command.options.map { |k, v| option_map[k.to_s] })
|
390
|
-
end
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
#
|
395
|
-
# Represents a user context menu interaction.
|
396
|
-
#
|
397
|
-
class UserMenuCommand < CommandInteraction
|
398
|
-
@command_type = 2
|
399
|
-
|
400
|
-
# @return [Discorb::Member, Discorb::User] The target user.
|
401
|
-
attr_reader :target
|
402
|
-
|
403
|
-
private
|
404
|
-
|
405
|
-
def _set_data(data)
|
406
|
-
@target = guild.members[data[:target_id]] || Discorb::Member.new(@client, @guild_id, data[:resolved][:users][data[:target_id].to_sym], data[:resolved][:members][data[:target_id].to_sym])
|
407
|
-
@client.commands.find { |c| c.name == data[:name] && c.type_raw == 2 }.block.call(self, @target)
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
#
|
412
|
-
# Represents a message context menu interaction.
|
413
|
-
#
|
414
|
-
class MessageMenuCommand < CommandInteraction
|
415
|
-
@command_type = 3
|
416
|
-
|
417
|
-
# @return [Discorb::Message] The target message.
|
418
|
-
attr_reader :target
|
419
|
-
|
420
|
-
private
|
421
|
-
|
422
|
-
def _set_data(data)
|
423
|
-
@target = Message.new(@client, data[:resolved][:messages][data[:target_id].to_sym].merge(guild_id: @guild_id.to_s))
|
424
|
-
@client.commands.find { |c| c.name == data[:name] && c.type_raw == 3 }.block.call(self, @target)
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
private
|
429
|
-
|
430
|
-
def _set_data(data)
|
431
|
-
@name = data[:name]
|
432
|
-
end
|
433
|
-
|
434
|
-
class << self
|
435
|
-
# @private
|
436
|
-
attr_reader :command_type
|
437
|
-
|
438
|
-
# @private
|
439
|
-
def make_interaction(client, data)
|
440
|
-
nested_classes.each do |klass|
|
441
|
-
return klass.new(client, data) if !klass.command_type.nil? && klass.command_type == data[:data][:type]
|
442
|
-
end
|
443
|
-
client.log.warn("Unknown command type #{data[:type]}, initialized CommandInteraction")
|
444
|
-
CommandInteraction.new(client, data)
|
445
|
-
end
|
446
|
-
|
447
|
-
# @private
|
448
|
-
def nested_classes
|
449
|
-
constants.select { |c| const_get(c).is_a? Class }.map { |c| const_get(c) }
|
450
|
-
end
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
#
|
455
|
-
# Represents auto complete interaction.
|
456
|
-
#
|
457
|
-
class AutoComplete < Interaction
|
458
|
-
@interaction_type = 4
|
459
|
-
@interaction_name = :auto_complete
|
460
|
-
|
461
|
-
def _set_data(data)
|
462
|
-
super
|
463
|
-
Sync do
|
464
|
-
name = data[:name]
|
465
|
-
options = nil
|
466
|
-
if (option = data[:options]&.first)
|
467
|
-
case option[:type]
|
468
|
-
when 1
|
469
|
-
name += " #{option[:name]}"
|
470
|
-
options = option[:options]
|
471
|
-
when 2
|
472
|
-
name += " #{option[:name]}"
|
473
|
-
if (option_sub = option[:options]&.first)
|
474
|
-
if option_sub[:type] == 1
|
475
|
-
name += " #{option_sub[:name]}"
|
476
|
-
options = option_sub[:options]
|
477
|
-
else
|
478
|
-
options = option[:options]
|
479
|
-
end
|
480
|
-
end
|
481
|
-
else
|
482
|
-
options = data[:options]
|
483
|
-
end
|
484
|
-
end
|
485
|
-
|
486
|
-
unless (command = @client.bottom_commands.find { |c| c.to_s == name && c.type_raw == 1 })
|
487
|
-
@client.log.warn "Unknown command name #{name}, ignoring"
|
488
|
-
next
|
489
|
-
end
|
490
|
-
|
491
|
-
option_map = command.options.map { |k, v| [k.to_s, v[:default]] }.to_h
|
492
|
-
options ||= []
|
493
|
-
options.each_with_index do |option|
|
494
|
-
val = case option[:type]
|
495
|
-
when 3, 4, 5, 10
|
496
|
-
option[:value]
|
497
|
-
when 6
|
498
|
-
guild.members[option[:value]] || guild.fetch_member(option[:value]).wait
|
499
|
-
when 7
|
500
|
-
guild.channels[option[:value]] || guild.fetch_channels.wait.find { |channel| channel.id == option[:value] }
|
501
|
-
when 8
|
502
|
-
guild.roles[option[:value]] || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
|
503
|
-
when 9
|
504
|
-
guild.members[option[:value]] || guild.roles[option[:value]] || guild.fetch_member(option[:value]).wait || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
|
505
|
-
end
|
506
|
-
option_map[option[:name]] = val
|
507
|
-
end
|
508
|
-
focused_index = options.find_index { |o| o[:focused] }
|
509
|
-
val = command.options.values[focused_index][:autocomplete]&.call(self, *command.options.map { |k, v| option_map[k.to_s] })
|
510
|
-
send_complete_result(val)
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
|
-
def send_complete_result(val)
|
515
|
-
@client.http.post("/interactions/#{@id}/#{@token}/callback", {
|
516
|
-
type: 8,
|
517
|
-
data: {
|
518
|
-
choices: val.map do |vk, vv|
|
519
|
-
{
|
520
|
-
name: vk,
|
521
|
-
value: vv,
|
522
|
-
}
|
523
|
-
end,
|
524
|
-
},
|
525
|
-
}).wait
|
526
|
-
rescue Discorb::NotFoundError
|
527
|
-
@client.log.warn "Failed to send auto complete result, This may be caused by the suggestion is taking too long (over 3 seconds) to respond", fallback: $stderr
|
528
|
-
end
|
529
|
-
|
530
|
-
class << self
|
531
|
-
alias make_interaction new
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
|
-
#
|
536
|
-
# Represents a message component interaction.
|
537
|
-
# @abstract
|
538
|
-
#
|
539
|
-
class MessageComponentInteraction < Interaction
|
540
|
-
include Interaction::SourceResponse
|
541
|
-
include Interaction::UpdateResponse
|
542
|
-
# @return [String] The content of the response.
|
543
|
-
attr_reader :custom_id
|
544
|
-
# @return [Discorb::Message] The target message.
|
545
|
-
attr_reader :message
|
546
|
-
|
547
|
-
@interaction_type = 3
|
548
|
-
@interaction_name = :message_component
|
549
|
-
|
550
|
-
# @private
|
551
|
-
def initialize(client, data)
|
552
|
-
super
|
553
|
-
@message = Message.new(@client, data[:message].merge({ member: data[:member] }))
|
554
|
-
end
|
555
|
-
|
556
|
-
class << self
|
557
|
-
# @private
|
558
|
-
attr_reader :component_type
|
559
|
-
|
560
|
-
# @private
|
561
|
-
def make_interaction(client, data)
|
562
|
-
nested_classes.each do |klass|
|
563
|
-
return klass.new(client, data) if !klass.component_type.nil? && klass.component_type == data[:data][:component_type]
|
564
|
-
end
|
565
|
-
client.log.warn("Unknown component type #{data[:component_type]}, initialized Interaction")
|
566
|
-
MessageComponentInteraction.new(client, data)
|
567
|
-
end
|
568
|
-
|
569
|
-
# @private
|
570
|
-
def nested_classes
|
571
|
-
constants.select { |c| const_get(c).is_a? Class }.map { |c| const_get(c) }
|
572
|
-
end
|
573
|
-
end
|
574
|
-
|
575
|
-
#
|
576
|
-
# Represents a button interaction.
|
577
|
-
#
|
578
|
-
class Button < MessageComponentInteraction
|
579
|
-
@component_type = 2
|
580
|
-
@event_name = :button_click
|
581
|
-
# @return [String] The custom id of the button.
|
582
|
-
attr_reader :custom_id
|
583
|
-
|
584
|
-
private
|
585
|
-
|
586
|
-
def _set_data(data)
|
587
|
-
@custom_id = data[:custom_id]
|
588
|
-
end
|
589
|
-
end
|
590
|
-
|
591
|
-
#
|
592
|
-
# Represents a select menu interaction.
|
593
|
-
#
|
594
|
-
class SelectMenu < MessageComponentInteraction
|
595
|
-
@component_type = 3
|
596
|
-
@event_name = :select_menu_select
|
597
|
-
# @return [String] The custom id of the select menu.
|
598
|
-
attr_reader :custom_id
|
599
|
-
# @return [Array<String>] The selected options.
|
600
|
-
attr_reader :values
|
601
|
-
|
602
|
-
# @!attribute [r] value
|
603
|
-
# @return [String] The first selected value.
|
604
|
-
|
605
|
-
def value
|
606
|
-
@values[0]
|
607
|
-
end
|
608
|
-
|
609
|
-
private
|
610
|
-
|
611
|
-
def _set_data(data)
|
612
|
-
@custom_id = data[:custom_id]
|
613
|
-
@values = data[:values]
|
614
|
-
end
|
615
|
-
end
|
616
|
-
end
|
1
|
+
%w[root response command components autocomplete].each do |file|
|
2
|
+
require_relative "interaction/#{file}.rb"
|
617
3
|
end
|
data/lib/discorb/log.rb
CHANGED
@@ -65,10 +65,11 @@ module Discorb
|
|
65
65
|
return
|
66
66
|
end
|
67
67
|
|
68
|
+
time = Time.now.iso8601
|
68
69
|
if @colorize_log
|
69
|
-
@out.puts("\e[
|
70
|
+
@out.puts("\e[90m#{time}\e[0m #{color}#{name.ljust(5)}\e[0m #{message}")
|
70
71
|
else
|
71
|
-
@out.puts("
|
72
|
+
@out.puts("#{time} #{name.ljust(5)} #{message}")
|
72
73
|
end
|
73
74
|
end
|
74
75
|
end
|
data/lib/discorb/message.rb
CHANGED
data/lib/discorb/modules.rb
CHANGED
@@ -142,6 +142,48 @@ module Discorb
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
|
+
#
|
146
|
+
# Fetch the pinned messages in the channel.
|
147
|
+
# @macro async
|
148
|
+
# @macro http
|
149
|
+
#
|
150
|
+
# @return [Async::Task<Array<Discorb::Message>>] The pinned messages in the channel.
|
151
|
+
#
|
152
|
+
def fetch_pins
|
153
|
+
Async do
|
154
|
+
_resp, data = @client.http.get("/channels/#{channel_id.wait}/pins").wait
|
155
|
+
data.map { |pin| Message.new(@client, pin) }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Pin a message in the channel.
|
161
|
+
# @macro async
|
162
|
+
# @macro http
|
163
|
+
#
|
164
|
+
# @param [Discorb::Message] message The message to pin.
|
165
|
+
# @param [String] reason The reason of pinning the message.
|
166
|
+
#
|
167
|
+
def pin_message(message, reason: nil)
|
168
|
+
Async do
|
169
|
+
@client.http.put("/channels/#{channel_id.wait}/pins/#{message.id}", {}, audit_log_reason: reason).wait
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
#
|
174
|
+
# Unpin a message in the channel.
|
175
|
+
# @macro async
|
176
|
+
# @macro http
|
177
|
+
#
|
178
|
+
# @param [Discorb::Message] message The message to unpin.
|
179
|
+
# @param [String] reason The reason of unpinning the message.
|
180
|
+
#
|
181
|
+
def unpin_message(message, reason: nil)
|
182
|
+
Async do
|
183
|
+
@client.http.delete("/channels/#{channel_id.wait}/pins/#{message.id}", audit_log_reason: reason).wait
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
145
187
|
#
|
146
188
|
# Trigger the typing indicator in the channel.
|
147
189
|
# @macro async
|
data/lib/discorb/rate_limit.rb
CHANGED
@@ -24,7 +24,7 @@ module Discorb
|
|
24
24
|
return if path.start_with?("https://")
|
25
25
|
|
26
26
|
if @global
|
27
|
-
time = b[:reset_at] - Time.now.
|
27
|
+
time = b[:reset_at] - Time.now.to_f
|
28
28
|
@client.log.info("global rate limit reached, waiting #{time} seconds")
|
29
29
|
sleep(time)
|
30
30
|
@global = false
|
@@ -54,7 +54,7 @@ module Discorb
|
|
54
54
|
#
|
55
55
|
def save(method, path, resp)
|
56
56
|
if resp["X-Ratelimit-Global"] == "true"
|
57
|
-
@global = Time.now.
|
57
|
+
@global = Time.now.to_f + JSON.parse(resp.body, symbolize_names: true)[:retry_after]
|
58
58
|
end
|
59
59
|
return unless resp["X-RateLimit-Remaining"]
|
60
60
|
|